一.条件变量
当一个线程互斥地访问某个变量时,可能发现在其他线程改变状态之前,它什么也做不了.例如,一个线程访问队列时,发现队列为空,它只能等待,直到其他线程将一个节点添加到队列中,这种情况需要条件变量.
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。
二. 条件变量函数
pthread_cond_init
pthread_cond_destroy
pthread_cond_wait
pthread_cond_signal
pthread_cond_broadcast
三. 条件变量使用规范
(1)等待条件代码
pthread_mutex_lock(&mutex); //锁定互斥量
while(条件为假)
pthread_cond_wait(cond,mutex);
修改条件
pthread_mutex_unlock(&mutex);
(2)给条件发送信号代码
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);
四. 使用条件变量解决生产者消费者问题
#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 4 pthread_cond_t g_cond; pthread_mutex_t g_mutex; // 创建的线程ID保存在g_thread中 pthread_t g_thread[CONSUMERS_COUNT+PRODUCERS_COUNT]; int nready=0; ///消费者 void* consume(void* arg) { int num = (int)arg; int i; while(1) { pthread_mutex_lock(&g_mutex); while(nready == 0) { printf("(%d)begin wait a condition....\n",num); pthread_cond_wait(&g_cond,&g_mutex); } printf("(%d) end wait a condition....\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 i; 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(5); } return NULL; } int main(void ) { int i=0; //初始化互斥锁 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; }
问题分析:
//消费者
while(1)
{
pthread_mutex_lock(&g_mutex);
while(nready == 0)
{
pthread_cond_wait(&g_cond,&g_mutex);
}
--nready;
pthread_mutex_unlock(&g_mutex);
}
//生产者
while(1)
{
pthread_mutex_lock(&g_mutex);
++nready;
if(nready>0)
pthread_cond_signal(&g_cond);
pthread_mutex_unlock(&g_mutex);
}
1. 分析pthread_cond_wait(&g_cond,&g_mutex);所做的3件事
(1)对g_mutex进行解锁,(让别的线程改变条件变量,达到满足条件)
(2)等待条件,直到有线程想它发起通知
(3)重新对g_mutex进行加锁操作
2. 分析为什么使用while,而不用if语句
pthread_cond_signal 会向第一个等待的线程发起通知,如果没有任何一个线程处理等待状态,这个通知将被忽略
pthread_cond_broadcast 向所有等待线程发起通知
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再次判断条件是否满足.