• 线程同步和条件变量生产者消费者模型


    线程同步

    1. 线程同步概念:

            线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。

    2. 锁的使用

    Linux 中提供一把互斥锁 mutex(也称之为互斥量)。
           每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
           资源还是共享的,线程间也还是竞争的。

     

    3. 主要应用函数:

           pthread_mutex_init                 初始化一个互斥锁 函数

           pthread_mutex_destory          销毁一个互斥锁 函数

           pthread_mutex_lock                加锁 函数

           pthread_mutex_trylock           尝试加锁 函数

           pthread_mutex_unlock            解锁 函数

    以上5个函数的返回值都是:成功返回0,失败返回错误号

            pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。pthread_mutex_t mutex;变量mutex只有两种取值:0,1

    4. 使用mutex(互斥量、互斥锁)一般步骤:

     

    初始化互斥量:

    pthread_mutex_t mutex;

    1. pthread_mutex_init(&mutex, NULL);                  动态初始化。

    2. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;      静态初始化。

    使用mutex(互斥量、互斥锁)一般步骤:

    1. pthread_mutex_t lock;  创建锁

    2 .pthread_mutex_init; 初始化            1

    3. pthread_mutex_lock;加锁               1--   --> 0

    4. 访问共享数据(stdout)           

    5. pthrad_mutext_unlock();解锁          0++ --> 1

    6. pthead_mutex_destroy;销毁锁

    5. 使用锁实现互斥访问共享区:

     

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. pthread_mutex_t mutex; // 定义一把互斥锁
    7. void *tfn(void *arg)
    8. {
    9. srand(time(NULL));
    10. while (1) {
    11. pthread_mutex_lock(&mutex); // 加锁
    12. printf("hello ");
    13. sleep(rand() % 3); // 模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误
    14. printf("world\n");
    15. pthread_mutex_unlock(&mutex); // 解锁
    16. sleep(rand() % 3);
    17. }
    18. return NULL;
    19. }
    20. int main(void)
    21. {
    22. pthread_t tid;
    23. srand(time(NULL));
    24. int ret = pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
    25. if(ret != 0){
    26. fprintf(stderr, "mutex init error:%s\n", strerror(ret));
    27. exit(1);
    28. }
    29. pthread_create(&tid, NULL, tfn, NULL);
    30. while (1) {
    31. pthread_mutex_lock(&mutex); // 加锁
    32. printf("HELLO ");
    33. sleep(rand() % 3);
    34. printf("WORLD\n");
    35. pthread_mutex_unlock(&mutex); // 解锁
    36. sleep(rand() % 3);
    37. }
    38. pthread_join(tid, NULL);
    39. pthread_mutex_destory(&mutex); // 销毁互斥锁
    40. return 0;
    41. }

    编译运行,结果如下:

     

    6. 两种死锁

    是使用锁不恰当导致的现象:

    1. 对一个锁反复lock。

    2. 两个线程,各自持有一把锁,请求另一把。

     

    条件变量

    1. 条件变量是什么?

    条件变量:

            条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

           主要应用函数:

    pthread_cond_t cond;

           初始化条件变量:

           1. pthread_cond_init(&cond, NULL);                                     动态初始化。

           2. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;      静态初始化。

    2. 阻塞函数wait和唤醒函数signal

    阻塞等待一个条件变量
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    函数作用:
    1. 阻塞等待条件变量 cond被唤醒,阻塞等待时mutex会解锁,被唤醒时mutex加锁;
    2. 释放已掌握的互斥锁(解锁互斥量) 相当于pthread_mutex_unlock(&mutex);
    3. 当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);

    pthread_cond_signal 函数
           唤醒至少一个阻塞在条件变量上的线程
           int pthread_cond_signal(pthread_cond_t *cond);

    pthread_cond_broadcast 函数
           唤醒全部阻塞在条件变量上的线程
           int pthread_cond_broadcast(pthread_cond_t *cond);

    3. 条件变量的生产者消费者模型分析

            线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。

            假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。

     4. 条件变量生产者消费者代码

    1. /*借助条件变量模拟 生产者-消费者 问题*/
    2. #include
    3. #include
    4. #include
    5. #include
    6. /*链表作为公享数据,需被互斥量保护*/
    7. struct msg {
    8. struct msg *next;
    9. int num;
    10. };
    11. struct msg *head;
    12. /* 静态初始化 一个条件变量 和 一个互斥量*/
    13. pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
    14. pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    15. void *consumer(void *p)
    16. {
    17. struct msg *mp;
    18. for (;;) {
    19. pthread_mutex_lock(&lock);
    20. while (head == NULL) { //头指针为空,说明没有节点 可以为if吗
    21. pthread_cond_wait(&has_product, &lock); // 解锁,并阻塞等待
    22. }
    23. mp = head;
    24. head = mp->next; //模拟消费掉一个产品
    25. pthread_mutex_unlock(&lock);
    26. printf("-Consume %lu---%d\n", pthread_self(), mp->num);
    27. free(mp);
    28. sleep(rand() % 5);
    29. }
    30. }
    31. void *producer(void *p)
    32. {
    33. struct msg *mp;
    34. for (;;) {
    35. mp = malloc(sizeof(struct msg));
    36. mp->num = rand() % 1000 + 1; //模拟生产一个产品
    37. printf("-Produce ---------------------%d\n", mp->num);
    38. pthread_mutex_lock(&lock);
    39. mp->next = head;
    40. head = mp;
    41. pthread_mutex_unlock(&lock);
    42. pthread_cond_signal(&has_product); //将等待在该条件变量上的一个线程唤醒
    43. sleep(rand() % 5);
    44. }
    45. }
    46. int main(int argc, char *argv[])
    47. {
    48. pthread_t pid, cid;
    49. srand(time(NULL));
    50. pthread_create(&pid, NULL, producer, NULL);
    51. pthread_create(&cid, NULL, consumer, NULL);
    52. pthread_join(pid, NULL);
    53. pthread_join(cid, NULL);
    54. return 0;
    55. }

     编译运行,结果如下:

  • 相关阅读:
    java-springboot基于机器学习得心脏病预测系统 的设计与实现-计算机毕业设计
    Hedgehog 信号通路与癌症
    设计模式 -- 代理模式(Proxy Pattern)
    ZnDPA-Cy7 荧光细胞凋亡检测凋亡靶向探针
    JavaWeb-使用session机制和cookie机制改造JavaWeb基础项目
    Java 遍历字符串 和 截取码点
    okcc呼叫中心的的录音功能
    vue2踩坑之项目:Swiper轮播图使用
    RRU-Net:The Ringed Residual U-Net for Image Splicing Forgery Detection阅读笔记一
    Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
  • 原文地址:https://blog.csdn.net/kakaka666/article/details/126014596