阅读了《Unix/Linux系统编程》一书中关于Pthread中的条件变量相关内容,进行了整理总结
本文包含了临界区、条件变量等内容,并关于pthread_cond_wait放在while还是if中通过示例进行了具体分析
参考资料
5. 条件变量
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,即提供了一种线程协作的方式。
5.1 条件变量初始化
Pthreads中pthread_cond_t
即为条件变量类型,与互斥量方法一样,条件变量有两种初始化方法。
-
静态方法:定义一个条件变量con,并使用默认属性对其进行初始化
pthread_cond_t con = PTHREAD_COND_INITIALIZER;
-
动态方法:使用
pthread_cond_init()
函数来初始化条件变量,通过第二个参数设置条件变量属性。第二个参数可以设置为NULL
来使用默认属性初始化条件变量int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t *attr)
pthread_cond_init(&con,NULL);
5.2 条件变量使用:
-
等待条件变量函数:
pthread_cond_wait
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
当线程调用
pthread_cond_wait()
函数时,该函数会阻塞调用线程,直到发出指定条件的信号。会将调用线程放到等待条件的线程列表上,并对互斥量解锁(这样就不会死锁)
。当pthread_cond_wait()
返回时(即收到信号后),互斥量再次锁住
。因为要根据共享变量的状态来决定是否等待(即互斥的访问共享变量),因此在该函数调用前要先获取相关互斥量(即正在互斥的访问该共享变量)。所以该函数必须要放在
pthread_mutex_lock
和pthread_mutex_unlock
之间的临界区内。该函数放在if还是while里面:
需要注意由于有可能同时唤醒多个正在等待该条件的线程。因此,如果
pthread_cond_wait
放在if里面而不是while里面,那么当被唤起两个线程时(考虑争夺仅有的一个资源时的情况),会发生第二个线程没有资源可以消费的情况,后续操作出现错误!因此,一定要使用while做进一步循环判断,这样即使两个线程都被唤起,那么第二个线程会再次判断资源是否可用,如果不可用,就再次pthread_cond_wait
。如while(resource == FALSE) pthread_cond_wait(&cond, &mutex)
这个例子将放在本节第二个示例中具体分析
-
通知线程条件满足:
pthread_cond_signal
和pthread_cond_broadcast
。int pthread_cond_signal(pthread_cond_t * cond)
该函数发出信号,唤醒至少一个正在等待该条件的线程。
pthread_cond_signal
既可以放在pthread_mutex_lock
和pthread_mutex_unlock
之间**(推荐)**,也可以放在pthread_mutex_lock
和pthread_mutex_unlock
之后,但是各有各的缺点。推荐使用第一种int pthread_cond_broadcast(pthread_cond_t * cond)
该函数会唤醒等待该条件的所有线程。
5.3 条件变量使用示例:简单(单一)生产者消费者问题
定义一个int类型全局变量num,并指定其上限。生产者线程不停往里面+1直到加到上限,然后就不能加了,要等消费者减后才能继续加;消费者线程不停往里面-1直到减到0,然后就不能减了,要等生产者加后才能继续减
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define MAX 5//共享变量最大值上界
int num;//全局共享变量
pthread_mutex_t * m;//互斥量
pthread_cond_t * push;//条件变量push:表示生产者生产了一个产品
pthread_cond_t * pop;//条件变量pop:表示消费者消费了一个产品
//消费者线程入口函数
void* consumer(void*arg){
for(int i = 0 ; i < 15 ; ++i){
pthread_mutex_lock(m);//上锁
if(num ==