• Day 57 条件变量


    1.条件变量:

    条件变量本身不是锁 ! 但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
    2.主要函数:
    pthread_cond_init 函数:
    初始化条件变量
    int pthread_cond_init(pthread_cond_t *restrict cond,
    const pthread_condattr *restrict attr);
    参1:需要初始化的条件变量
    参2: attr表条件变量属性,通常为默认值,传NULL即可
    初始化方式有两种:
    1. 动态初始化:pthread_cond_init(&cond,NULL)
    2. 静态初始化: pthread_cond_t cond = PTHREAD_COND_INITIALIZER ;
    pthread_cond_wait 函数:
    阻塞等待一一个条件变量
    int pthread_cond_wait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex);
    参1:已经初始化号的条件变量,如果条件不满足,则一直阻塞等待
    参2:一把已加锁的互斥锁,解锁已加锁的mutex,等价pthread_mutex_unlock(&mutex)
    参1、参2执行时是原子操作。(也就阻塞时,会继续操作解锁,中途不会被中断)
    函数返回return前:
    当条件变量必须满足,同步解除阻塞并重新给互斥量加pthread_mutex_lock(&mutex)
    pthread_cond_signal 函数
    唤醒等待在该条件变量上的一个线程
    pthread_cond_broadcast函数
    唤醒等待在该条件变量上的所有线程
    int pthread_cond_signal(&cond);
    参1:已经初始化好的条件变量
    3.生产消费模型:
    就是多进程对同一共享资源进行处理,一部分线程(生产者)处理完数据(商品),放进缓冲区(仓库),另一部分线程对处理好的数据(商品)进行处理(消费者),后者处理完之后,缓冲区(仓库)又有新的数据(商品),然后消费者继续消费,以此类推。这种算法的好处是效率高。
    代码如下:
    1. #include <stdlib.h>
    2. #include <unistd.h>
    3. #include <pthread.h>
    4. #include <stdio.h>
    5. #include <string.h>
    6. /*链表作为公享数据,需被互斥量保护*/
    7. #define SIZE 100
    8. int repository[SIZE]={0};//定义仓库大小,并初始化为0
    9. /*静态初始化一个条件变量和一个互斥量*/
    10. pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
    11. pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    12. int empty_repository(int arr[])
    13. {
    14. int i,sum=0;
    15. for(i=0;i<SIZE;i++)
    16. {
    17. sum += arr[i];
    18. }
    19. return sum;
    20. }
    21. int fill_repository(int arr[])
    22. {
    23. int i,sum;
    24. for(i=0;i<SIZE;i++)
    25. {
    26. if(arr[i]==0)
    27. {
    28. arr[i]= rand() % 1000 + 1;
    29. printf("-------fill a[%d]=%d\n",i,arr[i]);
    30. return arr[i];
    31. }
    32. }
    33. return -1;
    34. }
    35. int del_repository(int arr[])
    36. {
    37. int i,temp;
    38. for(i=SIZE;i>=0;i--)
    39. {
    40. if(arr[i]!=0)
    41. {
    42. temp=arr[i];
    43. printf("-------del a[%d]=%d\n",i,arr[i]);
    44. arr[i]= 0;
    45. return temp;
    46. }
    47. }
    48. return -1;
    49. }
    50. void *consumer(void *p)
    51. {
    52. for (;;)
    53. {
    54. int ret=pthread_mutex_lock(&lock);
    55. if(ret !=0)
    56. {
    57. printf("consumer mutex_lock err:%s\n",strerror(ret));
    58. }
    59. while (!empty_repository(repository))
    60. {
    61. //如果条件不满足,释放锁,并阻塞在此处
    62. //如果条件满足,重新加锁,并解除阻塞,进行循环条件判断
    63. printf("cond_wait test mask\n");
    64. pthread_cond_wait(&has_product, &lock);
    65. }
    66. //模拟消费掉一个产品
    67. ret=del_repository(repository);
    68. if(ret==-1)
    69. {
    70. printf("del_repository() err\n");
    71. pthread_exit(NULL);
    72. }
    73. pthread_mutex_unlock(&lock);
    74. printf("-Consume %lu---Produce id=%d\n", pthread_self(), ret);
    75. sleep(rand() % 5);
    76. }
    77. }
    78. void *producer(void *p)
    79. {
    80. for (; ;)
    81. { //sleep(5),为验证consumer线程中的,pthread_cond_wait()会进行解锁+阻塞功能
    82. sleep(5);
    83. //生产者拿锁成功,再生产产品
    84. int ret=pthread_mutex_lock(&lock);
    85. if(ret !=0)
    86. {
    87. printf("producer mutex_lock() err:%s\n",strerror(ret));
    88. pthread_mutex_unlock(&lock);
    89. break;//跳出循环
    90. }
    91. //模拟生产一个产品
    92. ret=fill_repository(repository);
    93. if(ret==-1)
    94. {
    95. printf("fill_repository() err\n");
    96. pthread_mutex_unlock(&lock);
    97. break;
    98. // pthread_exit(NULL);//生产仓库满后,无法继续生产,退出生产线程
    99. }
    100. pthread_mutex_unlock(&lock);
    101. printf("-producer %lu---Produce id=%d\n", pthread_self(), ret);
    102. pthread_cond_signal(&has_product); //条件满足了,通知等待条件变量has_product的线程
    103. usleep(100000);
    104. }
    105. }
    106. int main(int argc, char *argv[])
    107. {
    108. pthread_t tid01, tid02,tid03;
    109. srand(time(NULL));
    110. pthread_create(&tid01, NULL, producer, NULL);
    111. pthread_create(&tid02, NULL, consumer, NULL);
    112. // pthread_create(&tid03, NULL, consumer, NULL);
    113. pthread_join(tid01, NULL);
    114. pthread_join(tid02, NULL);
    115. // pthread_join(tid03, NULL);
    116. return 0;
    117. }

    4.信号量:

    概念信号量,是相对折中的一-种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

    基本操作

    sem_wait: 1. 信号量大于 0 ,则信号量 -- ( 类比 pthread_mutex_lock)
    2. 信号量等于 0 时,再次调用会造成线程阻塞。
    对应
    sem_post : 将信号量 ++ ,同时唤醒阻塞在信号量上的线程 ( 类比 pthread_mutex_unlock)
    但,由于 sem 的实现对用户隐藏,所以所谓的 ++. -- 操作只能通过函数来实现,而不能直接 ++ - 符号信号量的初值,决定了占用信号量的线程的个数。

    操作函数:

    sem_init 函数:
    初始化信号量
    int sem_init(sem_t *sem, int pshared, unsigned int vale)
    参1: sem 已定义的信号量
    参2: pshared 0 用于线程间同步1 用于进程间同步
    参3: vale N值,指定同时能够访问的线程数
    sem_destroy 函数:
    销毁信号量
    int sen_destroy(sem_t *sem);
    sem_wait 函数
    信号量减减操作 ( 类似加锁 )
    int sem_wait(sen_t *sem);
    sem_post 函数
    信号量加加操作 ( 类似解锁 )
    int sem_post(sen_t *sem);
    sem_timedwait 函数
    限时尝试对信号量加锁 --
    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
    参2: abs_timeout采用的是绝对时间(1970年1月1日0时0分0秒)。
    比如定时1秒:
    time_t cur = time(NULL);获取当前时间
    struct timespec t ;定义timespec结构体变量t
    t.tv_sec=cur+1;定时1秒。
    t.tv_nsec=t.tv_sec+100;
    sem_timedwait(&sem, &t);传参
    使用信号量之后,消费生产模型代码如下:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define NUM 5
    7. int queue[NUM];//全局数组实现环形队列
    8. sem_t blank_number, product_number ;//空格子信号量,产品信号量
    9. void *producer(void *arg)
    10. {
    11. int i=0;
    12. while (1)
    13. {
    14. sem_wait(&blank_number);//生产者将空格子数-- ,为0则阻塞等待
    15. queue[i] = rand() % 1000 + 1;//生产一个产品
    16. printf("----Produce---%d\n",queue[i]);
    17. sem_post(&product_number);//将产品数++
    18. i=(i+1)%NUM;//借助下标实现环形
    19. sleep( rand()%1);
    20. }
    21. }
    22. void *consumer(void *arg)
    23. {
    24. int i=0;
    25. while (1)
    26. {
    27. sem_wait(&product_number);//消费者将产品数--,为则阻塞等待
    28. printf("-Consume---%d\n" , queue[i]);
    29. queue[i] = 0;//消费一个产品
    30. sem_post(&blank_number);//消费掉以后,将空格子数++
    31. i = (i+1) % NUM;
    32. sleep(rand( )%3);
    33. }
    34. }
    35. int main(int argc, char *argv[])
    36. {
    37. pthread_t pid, cid;
    38. sem_init(&blank_number, 0, NUM); //初始化线程间共享-0,空格子信号量为5,
    39. sem_init(&product_number, 0,0);//初始化线程间共享-0,产品数为0
    40. pthread_create(&pid, NULL, producer, NULL);
    41. pthread_create(&cid, NULL, consumer, NULL);
    42. pthread_join(pid, NULL);
    43. pthread_join(cid, NULL);
    44. sem_destroy(&blank_number );
    45. sem_destroy(&product_number);
    46. return 0;
    47. }

  • 相关阅读:
    MES系统在制造业中的SOP工艺标准管理
    基于改进灰狼优化算法的边缘计算任务调度方法
    【STL】set/multiset容器
    JS严格模式(精简分析,快速掌握)
    Entity Developer数据库应用程序的开发
    谷歌Freshness新鲜度算法:如何利用它提升网站排名?
    week9|查阅文章 Mac os M2芯片 arm64架构|安装paddlepaddle问题
    第十四章·享元模式
    如何搭建跨境独立站?
    goland快捷键
  • 原文地址:https://blog.csdn.net/m0_60247706/article/details/127638026