用条件变量来解决生产者消费者问题
2015-08-11 23:09
267 查看
在上一篇文章中,我使用了匿名信号量
什么是条件变量?
我们可以设想一个场景:线程A需要某个条件成立才能继续执行,否则一直等待下去,而线程B执行过程中使线程的执行条件成立,并且唤醒A。
举个例子,在生产者消费者模型中,消费者如果看到缓冲区为空时,就等待,而生产者往缓冲区添加完数据后,唤醒消费者。
其中这个场景中,可以POSIX的条件变量来实现。
以下是条件变量所需要的函数
这里大家就百度怎么用,或者在Linux中用man 3 pthread_cond_***的方法也行。
其中重点讲一下的是 pthread_cond_wait这个函数。
条件变量是要和互斥量配合使用的,而pthread_cond_wait的搭配互斥量后是执行者三步操作。
第一,条件不成立时,释放互斥量(将互斥量解锁)。
第二,阻塞自己,让别的线程开始执行。
第三,当被其他的线程唤醒时,重新获得互斥量(重新将互斥量加锁),并执行后续代码。
掌握了这个之后,就来说下条件变量的使用规范。
第一,等待条件代码
第二,修改条件代码
在这里的一个难点是,第一段条件等待代码为什么不用if而用while呢?在man 3 pthread_cond_wait 之后发现这样一段话
If a signal is delivered to a thread waiting for a condition variable, upon return from the signal handler the thread resumes waiting for the condition variable as if it was not interrupted, or it shall return zero due to spurious wakeup.
也就是说,唤醒条件的信号,可以唤醒多个线程,但是只能允许一个信号访问,也就是说,因此等待线程需要不断的用while轮询一直到达到条件了才行。
好了,讲了这么多我们用它来做个生产者消费者实例吧。
最后,下图为代码执行结果。
什么是条件变量?
我们可以设想一个场景:线程A需要某个条件成立才能继续执行,否则一直等待下去,而线程B执行过程中使线程的执行条件成立,并且唤醒A。
举个例子,在生产者消费者模型中,消费者如果看到缓冲区为空时,就等待,而生产者往缓冲区添加完数据后,唤醒消费者。
其中这个场景中,可以POSIX的条件变量来实现。
以下是条件变量所需要的函数
pthread_cond_init pthread_cond_destroy pthread_cond_wait pthread_cond_signal pthread_cond_broadcast
这里大家就百度怎么用,或者在Linux中用man 3 pthread_cond_***的方法也行。
其中重点讲一下的是 pthread_cond_wait这个函数。
条件变量是要和互斥量配合使用的,而pthread_cond_wait的搭配互斥量后是执行者三步操作。
第一,条件不成立时,释放互斥量(将互斥量解锁)。
第二,阻塞自己,让别的线程开始执行。
第三,当被其他的线程唤醒时,重新获得互斥量(重新将互斥量加锁),并执行后续代码。
掌握了这个之后,就来说下条件变量的使用规范。
第一,等待条件代码
pthread_mutex_lock(&mutex); while (条件为假) pthread_cond_wait(cond, mutex); 修改条件; pthread_mutex_unlock(&mutex);
第二,修改条件代码
pthread_mutex_lock(&mutex); 设置条件为真 pthread_cond_signal(cond); pthread_mutex_unlock(&mutex);
在这里的一个难点是,第一段条件等待代码为什么不用if而用while呢?在man 3 pthread_cond_wait 之后发现这样一段话
If a signal is delivered to a thread waiting for a condition variable, upon return from the signal handler the thread resumes waiting for the condition variable as if it was not interrupted, or it shall return zero due to spurious wakeup.
也就是说,唤醒条件的信号,可以唤醒多个线程,但是只能允许一个信号访问,也就是说,因此等待线程需要不断的用while轮询一直到达到条件了才行。
好了,讲了这么多我们用它来做个生产者消费者实例吧。
#include <unistd.h> #include <sys/types.h> #include <pthread.h> #include <semaphore.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) #define CONSUMERS_COUNT 2 #define PRODUCERS_COUNT 1 pthread_mutex_t g_mutex; pthread_cond_t g_cond; pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT]; int nready = 0; void *consume(void *arg) { int num = (int)arg; while (1) { pthread_mutex_lock(&g_mutex); while (nready == 0) { printf("%d begin wait a condtion ...\n", num); pthread_cond_wait(&g_cond, &g_mutex); } printf("%d end wait a condtion ...\n", num); printf("%d begin consume product ...\n", num); --nready; printf("%d end consume product ...\n", num); pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL; } void *produce(void *arg) { int num = (int)arg; while (1) { pthread_mutex_lock(&g_mutex); printf("%d begin produce product ...\n", num); ++nready; printf("%d end produce product ...\n", num); pthread_cond_signal(&g_cond); printf("%d signal ...\n", num); pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL; } int main(void) { int i; pthread_mutex_init(&g_mutex, NULL); pthread_cond_init(&g_cond, NULL); for (i = 0; i < CONSUMERS_COUNT; i++) pthread_create(&g_thread[i], NULL, consume, (void *)i); sleep(1); for (i = 0; i < PRODUCERS_COUNT; i++) pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i); for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; i++) pthread_join(g_thread[i], NULL); pthread_mutex_destroy(&g_mutex); pthread_cond_destroy(&g_cond); return 0; }
最后,下图为代码执行结果。
相关文章推荐
- scala的list源码解密
- jQuery版感应鼠标显示隐藏的菜单
- Linux学习笔记:常用命令总结
- Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数(转)
- eclipse启动Tomcat服务输入http://localhost:8080/报404解决方法
- 简单的学生类
- 网址保存
- 【LeetCode】169 - Majority Element
- 使用U盘安装Centos 6.5操作系统,磁盘分区过程中出现,"sda必须有一个gpt磁盘标签"
- 1045 access denied for user 'root'@'localhost' using password yes
- hdu 2199 Can you solve this equation?
- OC第一节
- cocos ide 出现连接ide超时的原因和解决办法
- “rmdir: failed to remove ‘tmp’: Directory not empty”解决方案–Linux命令行如何删除非空文件夹
- css进阶学习
- 使用elasticsearch与kibana来分析nginx日志小结
- IOS中使用本地通知为你的APP添加提示用户功能
- arm汇编输出
- 微信公众平台赞赏功能真的要来了 已进入邀请内测阶段
- 【转载】LINUX 和 WINDOWS 内核的区别