Linux多线程实践(六)使用Posix条件变量解决生产者消费者问题
前面的一片文章我们已经讲过使用信号量解决生产者消费者问题。那么什么情况下我们须要引入条件变量呢?
这里借用 http://www.cnblogs.com/ngnetboy/p/3521547.html 的解释:
如果有共享的资源sum,与之相关联的mutex 是lock_s.如果每一个线程对sum的操作非常easy的,与sum的状态无关,比方仅仅是sum++.那么仅仅用mutex足够了.程序猿仅仅要确保每一个线程操作前,取得lock,然后sum++,再unlock就可以.每一个线程的代码将像这样:
add()
{
pthread_mutex_lock(lock_s);
sum++;
pthread_mutex_unlock(lock_s);
}
假设操作比較复杂,假设线程t0,t1,t2的操作是sum++,而线程t3则是在sum到达100的时候,打印出一条信息,并对sum清零. 这样的情况下,假设仅仅用mutex, 则t3须要一个循环,每一个循环里先取得lock_s,然后检查sum的状态,假设sum>=100,则打印并清零,然后unlock.假设sum<100,则unlock,并sleep()本线程合适的一段时间。
这个时候,t0,t1,t2的代码不变,t3的代码例如以下:
print()
{
while (1)
{
pthread_mutex_lock(lock_s);
if(sum<100)
{
printf(“sum reach 100!”);
pthread_mutex_unlock(lock_s);
}
else
{
pthread_mutex_unlock(lock_s);
my_thread_sleep(100);
return OK;
}
}
}
这样的办法有两个问题
1) sum在大多数情况下不会到达100,那么对t3的代码来说,大多数情况下,走的是else分支,仅仅是lock和unlock,然后sleep().这浪费了CPU处理时间.
2) 为了节省CPU处理时间,t3会在探測到sum没到达100的时候sleep()一段时间.这样却又带来另外一个问题,亦即t3响应速度下降.可能在sum到达200的时候,t4才会醒过来.
3) 这样,程序猿在设置sleep()时间的时候陷入两难境界,设置得太短了节省不了资源,太长了又减少响应速度.真是难办啊!
这个时候,condition variable,从天而降,解救了焦头烂额的你.
你首先定义一个condition variable.
pthread_cond_t cond_sum_ready=PTHREAD_COND_INITIALIZER;
t0,t1,t2的代码仅仅要后面加两行,像这样:
add()
{
pthread_mutex_lock(lock_s);
sum++;
pthread_mutex_unlock(lock_s);
if(sum>=100)
pthread_cond_signal(&cond_sum_ready);
}
而t3的代码则是
{
pthread_mutex_lock(lock_s);
while(sum<100)
pthread_cond_wait(&cond_sum_ready, &lock_s);
printf(“sum is over 100!”);
sum=0;
pthread_mutex_unlock(lock_s);
return OK;
}
注意两点:
1) 在thread_cond_wait()之前,必须先lock相关联的mutex, 由于假如目标条件未满足,pthread_cond_wait()实际上会unlock该mutex, 然后block,在目标条件满足后再又一次lock该mutex, 然后返回.
2) 为什么是while(sum<100),而不是if(sum<100) ?这是由于在pthread_cond_signal()和pthread_cond_wait()返回之间,有时间差,如果在这个时间差内,还有另外一个线程t4又把sum降低到100下面了,那么t3在pthread_cond_wait()返回之后,显然应该再检查一遍sum的大小.这就是用 while的用意
线程间的同步技术。主要以相互排斥锁和条件变量为主,条件变量和相互排斥所的配合使用能够非常好的处理对于条件等待的线程间的同步问题
Posix条件变量经常使用API:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
通常条件变量须要和相互排斥锁同一时候使用, 利用相互排斥量保护条件变量;条件的检測是在相互排斥锁的保护下进行的。
假设一个条件为假,一个线程自己主动堵塞,并释放等待状态改变的相互排斥锁。假设还有一个线程改变了条件,它就发送信号给关联的条件变量, 并唤醒一个或多个等待在该条件变量上的线程,这些线程将又一次获得相互排斥锁,又一次评价条件。假设将条件变量放到共享内存中, 而两进程可共享读写这段内存,则条件变量能够被用来实现两进程间的线程同步。
条件变量的使用规范:
(一)、等待条件代码
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);
注意是while而不是if,原因是在信号的中断后还能正常执行。
解决生产者消费者问题(无界缓冲区):
#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;
}
Linux多线程实践(六)使用Posix条件变量解决生产者消费者问题的更多相关文章
- Linux多线程实践(8) --Posix条件变量解决生产者消费者问题
Posix条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_co ...
- 并发编程(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...
- POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t 和pthread_cond_t的一系列的封装.因此通过 ...
- 并发编程入门(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...
- 第四十章 POSIX条件变量
条件变量 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了 例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中.这种情况就需要用到条件 ...
- POSIX条件变量
条件变量: 当一个线程互斥的访问某个变量时,它可能发现其他线程改变状态之前,它什么都做不了例如:一个线程访问队列时,发现队列为空,它只能等待,直到其他线程将一个节点添加到队列中,这种情况就需要使用条件 ...
- posix 条件变量与互斥锁 示例生产者--消费者问题
一.posix 条件变量 一种线程间同步的情形:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行. 在pthr ...
- python条件变量之生产者与消费者操作实例分析
python条件变量之生产者与消费者操作实例分析 本文实例讲述了python条件变量之生产者与消费者操作.分享给大家供大家参考,具体如下: 互斥锁是最简单的线程同步机制,面对复杂线程同步问题,Pyth ...
- java多线程解决生产者消费者问题
import java.util.ArrayList; import java.util.List; /** * Created by ccc on 16-4-27. */ public class ...
随机推荐
- 【【henuacm2016级暑期训练】动态规划专题 H】Greenhouse Effect
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 原题意等价于:给你一个序列(实数的位置没用!)..你可以改变其中某些元素的位置(插入到某些位置中间. 然后让他变成有序的. (有序的 ...
- 【codeforces 505D】Mr. Kitayuta's Technology
[题目链接]:http://codeforces.com/problemset/problem/505/D [题意] 让你构造一张有向图; n个点; 以及所要求的m对联通关系(xi,yi) 即要求这张 ...
- jvm 虚拟机的组成部分
1.类加载子系统 :负责从文件系统或者网络中加载 Class 信息,加载的信息存放在 一块称之为方法区的内存空间 2.方法区:存放类信息,常量信息,常量池信息,包括字符串字面量和数字常量等 3.Jav ...
- Linux Mint 17.1 安装全配置
Linux Mint 17.1 安装全配置 I. 前言 由于自己的本子出现了一些故障需要重新安装系统,就上网看看今年4,5月份发布的一些新的发行版来试试.原先电脑上安装的是opensuse13.2, ...
- 洛谷 P1278 单词游戏
P1278 单词游戏 题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单 ...
- HDU 4303 Contest 1
说实话,挺复杂的一道题. 我采用栈的方式,DFS在搜索完一个节点的所有子结点后,通过排序,加快计算该结点所有可能的路径:子结点与子结点的连通,子结点与父结点的连通,通过父结点与各祖先结点的连通.同时记 ...
- 安全风控的CAP原理和BASE思想
CAP原理最多实现两个,需要牺牲一个来满足其他两个:
- 计算机网络 4.网络层与IP协议
网络中的每一台主机和路由器都有一个网络层部分.而路由器中也没有网络层以上的层次.网络层是协议栈中最复杂的层次. 转发forwarding:当一个分组到达某路由器的输入链路时.该路由器将分组移动到适当的 ...
- hdu 4966 最小树形图
将每门课等级拆成0,1,2,3...a[i]个点,对每一个等级大于0的点向它低一级连边,权值为0[意思是,若修了level k.则level(0~k)都当做修了] 将输入的边建边,权值为money[i ...
- 不仅仅是Google,您必须知道的全球十大地图API
不仅仅是Google,您必须知道的全球十大地图API 一.总结 一句话总结:除了google,也有其它很多很好的地图,必应地图(Bing Maps),OpenLayers 二.不仅仅是Google,您 ...