boost的mutex,condition_variable非常好用。但是在Linux上,boost实际上做的是对pthread_mutex_t 和pthread_cond_t的一系列的封装。因此通过对原生态的POSIX 的mutex,cond的生成者,消费者的实现,我们可以再次体会boost带给我们的便利。

1. 什么是互斥量

互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线
程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以
对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。

pthread_mutex_t 就是POSIX对于mutex的实现。

函数名 参数 说明
pthread_mutex_init

pthread_mutex_t * mutex,

constpthread_mutex_t *attr

初始化一个互斥量,静态方式可以直接使用PTHREAD_MUTEX_INITIALIZER进行赋值初始化
pthread_mutex_destroy pthread_mutex_t *mutex 释放对互斥变量分配的资源。注意pthread_mutex_init有可能malloc了资源
pthread_mutex_lock pthread_mutex_t *mutex 如果互斥量已经上锁,调用线程阻塞直至互斥量解锁
pthread_mutex_trylock pthread_mutex_t *mutex 加锁,如果失败不阻塞
pthread_mutex_unlock pthread_mutex_t *mutex 解锁

使用init函数进行初始化:

  1. #include <pthread.h>
  2. pthread_mutex_t foo_mutex;
  3. void foo()
  4. {
  5. pthread_mutex_init(&foo_mutex, NULL);
  6. pthread_mutex_lock(&foo_mutex);
  7. /* Do work. */
  8. pthread_mutex_unlock(&foo_mutex);
  9. pthread_mutex_destroy(&foo_mutex);
  10. }

当然该初始化

  1. pthread_mutex_init(&foo_mutex, NULL);

只能foo_mutex使用前初始化一次,最后destroy。初始化已经初始化的mutex将导致undefined behavior。

另外一种用法:

  1. pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;
  2. void foo()
  3. {
  4. pthread_mutex_lock(&foo_mutex);
  5. /* Do work. */
  6. pthread_mutex_unlock(&foo_mutex);
  7. }


然了,这两种用法都有问题:如果在lock住后unlock之前出现exception,那么这个锁永远也不能unlock。这种情况下需要guard这
个资源。具体可参照boost::mutex::scoped_lock的实现,非常简单但是极大简化了mutex的安全使用。

2. 什么是条件变量

与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条
件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

条件变量的初始化和mutex的初始化差不多,也是有两种方式:

pthread_cond_tmy_condition=PTHREAD_COND_INITIALIZER;

也可以利用函数pthread_cond_init动态初始化。

下面中各个函数的简介。

函数名 参数 说明
pthread_cond_init pthread_cond_t *cond,
const pthread_condattr_t *attr
初始化
pthread_cond_destroy pthread_cond_t *cond 回收
pthread_cond_wait pthread_cond_t *cond,
pthread_mutex_t *mutex
等待,无超时
pthread_cond_timedwait pthread_cond_t *cond,pthread_mutex_t *mutex,
const struct timespec *abstime
等待,有超时
pthread_cond_signal pthread_cond_t *cond 一个在相同条件变量上阻塞的线程将被解锁。如果同时有多个线程阻塞,则由调度策略确定接收通知的线程
pthread_cond_broadcast pthread_cond_t *cond 将通知阻塞在这个条件变量上的所有线程。一旦被唤醒,线程仍然会要求互斥锁。

一个简单使用条件变量进行线程同步的小例子:

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  5. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  6. void *thread1(void *);
  7. void *thread2(void *);
  8. int i=1;
  9. int main(void)
  10. {
  11. pthread_t t_a;
  12. pthread_t t_b;
  13. pthread_create(&t_a,NULL,thread2,(void *)NULL);/*create thread t_a*/
  14. pthread_create(&t_b,NULL,thread1,(void *)NULL); /*create thread t_b*/
  15. pthread_join(t_b, NULL);/*wait for exit of t_b*/
  16. pthread_join(t_a, NULL);
  17. pthread_mutex_destroy(&mutex);
  18. pthread_cond_destroy(&cond);
  19. exit(0);
  20. }
  21. void *thread1(void *junk)
  22. {
  23. for(i=1;i<=9;i++)
  24. {
  25. pthread_mutex_lock(&mutex);
  26. if(i%3==0)
  27. pthread_cond_signal(&cond);
  28. else
  29. printf("thread1 running, i = %d\n",i);
  30. pthread_mutex_unlock(&mutex);
  31. sleep(1);
  32. }
  33. }
  34. void *thread2(void *junk)
  35. {
  36. while(i<9)
  37. {
  38. pthread_mutex_lock(&mutex);
  39. if(i%3!=0)
  40. pthread_cond_wait(&cond,&mutex);/*..*/
  41. printf("thread2 running, i = %d\n",i);
  42. pthread_mutex_unlock(&mutex);
  43. sleep(1);
  44. }
  45. }

输出:

  1. thread1 running, i = 1
  2. thread1 running, i = 2
  3. thread2 running, i = 3
  4. thread1 running, i = 4
  5. thread1 running, i = 5
  6. thread2 running, i = 6
  7. thread1 running, i = 7
  8. thread1 running, i = 8
  9. thread2 running, i = 9

3. 生产者-消费者的实现

        生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的
线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消
费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX 5
  4. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥锁*/
  5. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/
  6. typedef struct{
  7. char buffer[MAX];
  8. int how_many;
  9. }BUFFER;
  10. BUFFER share={"",0};
  11. char ch='A';/*初始化ch*/
  12. void *producer(void *);
  13. void *consumer(void *);
  14. int main(void)
  15. {
  16. pthread_t t_read;
  17. pthread_t t_write;
  18. pthread_create(&t_write,NULL,producer,(void *)NULL); /*创建进程t_a*/
  19. pthread_create(&t_read,NULL,consumer,(void *)NULL); /*创建进程t_b*/
  20. pthread_join(t_write,(void **)NULL);
  21. pthread_join(t_read, NULL);
  22. exit(0);
  23. }
  24. void *producer(void *junk)
  25. {
  26. int n=0;
  27. printf("Producer: starting\n");
  28. while(ch!='K')
  29. {
  30. pthread_mutex_lock(&mutex);/*锁住互斥量*/
  31. if(share.how_many!=MAX)
  32. {
  33. share.buffer[share.how_many++]=ch++;/*把字母写入缓存*/
  34. printf("Producer: put char[%c]\n",ch-1);/*打印写入字母*/
  35. if(share.how_many==MAX)
  36. {
  37. printf("Producer: signaling full\n");
  38. pthread_cond_signal(&cond);/*如果缓存中的字母到达了最大值就发送信号*/
  39. }
  40. }
  41. pthread_mutex_unlock(&mutex);/*解锁互斥量*/
  42. }
  43. sleep(1);
  44. printf("Producer:Exiting\n");
  45. return NULL;
  46. }
  47. void *consumer(void *junk)
  48. {
  49. int i;
  50. int n=0;
  51. printf("Consumer: starting\n");
  52. while(ch!='K')
  53. {
  54. pthread_mutex_lock(&mutex);/*锁住互斥量*/
  55. printf("\nConsumer : Waiting\n");
  56. while(share.how_many!=MAX)/*如果缓存区字母不等于最大值就等待*/
  57. pthread_cond_wait(&cond,&mutex);
  58. printf("Consumer: getting buffer:: ");
  59. for(i=0;share.buffer[i]&&share.how_many;++i,share.how_many--)
  60. putchar(share.buffer[i]); /*循环输出缓存区字母*/
  61. putchar('\n');
  62. pthread_mutex_unlock(&mutex);/*解锁互斥量*/
  63. }
  64. printf("Consumer: exiting\n");
  65. return NULL;
  66. }

输出:Producer: starting
Producer: put char[A]
Producer: put char[B]
Producer: put char[C]
Producer: put char[D]
Producer: put char[E]
Producer: signaling full
Consumer: starting

Consumer : Waiting
Consumer: getting buffer:: ABCDE

Consumer : Waiting
Producer: put char[F]
Producer: put char[G]
Producer: put char[H]
Producer: put char[I]
Producer: put char[J]
Producer: signaling full
Consumer: getting buffer:: FGHIJ
Consumer: exiting
Producer:Exiting

来源:http://blog.csdn.net/anzhsoft/article/details/19044069

http://blog.csdn.net/anzhsoft2008/article/category/2123999

POSIX 使用互斥量和条件变量实现生产者/消费者问题的更多相关文章

  1. 并发编程(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题

    boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...

  2. 并发编程入门(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题

    boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...

  3. 并发编程(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题

    请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...

  4. 并发编程入门(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题

    请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...

  5. 生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用

    全局初始化互斥量和条件变量(不全局也行,但至少要对线程启动函数可见,这样才能使用.) static pthread_cont_t cond = PTHREAD_COND_INITIALIZER; st ...

  6. [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

    一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...

  7. Linux多线程实践(8) --Posix条件变量解决生产者消费者问题

    Posix条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_co ...

  8. 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)

    1.Linux“线程” 进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种“多进程单线程”的操作系统.Linux本身只有进程的概念,而其所谓的“线程” ...

  9. 【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)

    上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html   现在有这篇文章: http://blog.cs ...

随机推荐

  1. 安全漏洞XSS、CSRF、SQL注入以及DDOS攻击

    随着互联网的普及,网络安全变得越来越重要,程序员需要掌握最基本的web安全防范,下面列举一些常见的安全漏洞和对应的防御措施. 0x01: XSS漏洞 1.XSS简介 跨站脚本(cross site s ...

  2. SoapUI: 从属性变量里面读取值, 把string 转换成arraylist

  3. Could not create connection to database server. Attempted reconnect 3 times. Giving up.

    报出这个错误,可能原因: 1.检查MySQL数据库服务是否正常(包含检查服务名和密码),如果不正常,修复至正常为止: 2.maven工程中导入的mysql的jar版本和你的MySQL版本不相符,必须相 ...

  4. 查找算法(7)--Hash search--哈希查找

    1.哈希查找 (1)什么是哈希表(Hash) 我们使用一个下标范围比较大的数组来存储元素.可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用 ...

  5. Git采坑与问题排查

    目录 Case1:代码库中存大文件 Case1:代码库中存大文件 背景 有这种情况,项目运行需要依赖一个文件(比如需要读一个文件中的数据),那么最直接的方式就是将该文件添加到代码中的分支中,然后将分支 ...

  6. 【Docker学习之五】Docker自定义镜像示例

    环境 docker-ce-19.03.1-3.el7.x86_64 centos 7 一.创建centos+jdk+tomcat镜像 对于公用的容器比如,tomcat.nginx.mysql等应用组件 ...

  7. 解决chrome浏览器插件开发者模式每次启动要确认弹出框的问题

    在日常工作中,我们经常会用到一些浏览器插件,有些插件因为没上架到浏览器的应用商店,只能以开发者模式运行,但是chrome浏览器出了限制,每次重新启动浏览器的时候,就会弹出该插件是否要禁止运行的对话框, ...

  8. ThreadLocal源代码2

    private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } private stat ...

  9. GitBook的使用

    首先安装gitbook npm install -g gitbook-cli 检查是否安装成功 gitbook -V 然后就要建立一个文件夹进到文件夹目录下 让此文件夹初始化下 gitbook ini ...

  10. Linux下Ngnix的安装与配置

    由于我的博客项目在8084端口,需要Nginx来转发一下端口,记录一下安装过程和踩过的小坑. 一.下载 wget http://nginx.org/download/nginx-1.12.2.tar. ...