条件变量:
当一个线程互斥的访问某个变量时,它可能发现其他线程改变状态之前,它什么都做不了
例如:一个线程访问队列时,发现队列为空,它只能等待,直到其他线程将一个节点添加到队列中,这种情况就需要使用条件变量。
线程A: 线程B:
改变n
int n=0 进入临界区
进入临界区
更改 n>0
等待 n>0(该线程进入临界区其他的线程无法运行) 通知等待线程
操作n 退出临界区
退出临界区
注意:当一个线程进入临界区,等待n>0,其余的线程无法获取互斥量(同一个互斥量)进入临界区修改n,那么线程A就会一直等待。
就会出现死锁,所以我们引入了条件变量。条件变量要跟互斥量配合使用,一是因为多个线程都可以访问条件n>0;而是因为一个线程
对互斥量加锁,其他线程就无法获得互斥量修改n.所以条件变量一开始先解锁,那么线程就可以获得互斥量,修改n.也可以等待条件。
条件变量使用规范
等待条件代码:
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);
通知条件成立
int pthread_cond_signal(pthread_cond_t *cond);
等待条件成立
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
while(1)
{
pthread_mutex_lock(&g_mutex);
//为何使用while而不是if
//被信号打断的话,一种pthread_cond_wait重启,就行没有发生一样,或者pthread_cond_wait可能被虚假唤醒,所以要用while再次判断条件。
while(nready==0)
{
pthread_cond_wait(&g_cond,&g_mutex);
}
--nready;
pthread_mutex_unlock(&g_mutex);
}
pthread_cond_wait(原语)内部进行的操作:
一、对g_mutex进行解锁(其他消费者线程可以进行等待,生产者线程也可以改变条件)
二、等待条件,直到其他线程向它发起通知
三、返回的时候对互斥量重新加锁
while(1)
{
pthread_mutex_lock(&g_mutex);
nready++;
if(nready>0)
pthread_cond_signal(&g_cond);//条件满足,发起通知
pthread_mutex_unlock(&g_mutex);
}
pthread_cond_signal
向第一个等待条件的线程发起通知,如果没有任何一个线程处于等待条件的状态,这个通知将被忽略
pthread_cond_broadcast
向所有的等待线程发起通知
用条件变量解决生产者、消费者问题(缓冲区无界,假定仓库无限大)
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<fcntl.h> 4 #include<sys/stat.h> 5 #include<stdlib.h> 6 #include<stdio.h> 7 #include<errno.h> 8 #include <semaphore.h> 9 #include<pthread.h> 10 #define ERR_EXIT(m)\ 11 do\ 12 {\ 13 perror(m);\ 14 exit(EXIT_FAILURE);\ 15 }while(0) 16 #define CONSUMERS_COUNT 2 17 #define PRODUCERS_COUNT 4 18 19 unsigned short in=0; 20 unsigned short out=0; 21 unsigned short produce_id=0;//当前正在消费的产品ID 22 unsigned short consume_id=0;//当前正在消费的产品ID 23 24 pthread_cond_t g_cond; 25 pthread_mutex_t g_mutex; 26 pthread_t g_thread[CONSUMERS_COUNT+PRODUCERS_COUNT ]; 27 28 //消费者线程 29 int nready=0;//缓冲区没有产品 30 void* consume(void*arg) 31 { 32 int num=(int)arg;//线程编号 33 34 while(1) 35 { 36 pthread_mutex_lock(&g_mutex); 37 //当条件不成立,就一直等待。 38 while(nready==0) 39 { 40 printf("%d begin wait a condition...\n",num); 41 //跟互斥锁配合使用,先解锁,以便其他线程进入临界区修改条件,防止死锁。 42 pthread_cond_wait(&g_cond,&g_mutex);//在nready==0上等待。 43 //再加锁 44 } 45 //条件改变了 46 printf("%d end wait a condition...\n",num); 47 printf("%d begin consume product...\n",num); 48 --nready;//消费产品 49 pthread_mutex_unlock(&g_mutex); 50 sleep(1); 51 } 52 return NULL; 53 } 54 //生产者线程 55 void* produce(void*arg) 56 { 57 int num=(int)arg; 58 59 while(1) 60 { 61 pthread_mutex_lock(&g_mutex); 62 printf("%d begin produce product...\n",num); 63 nready++;//直接开始生产产品。 64 printf("%d end produce product...\n",num); 65 //条件成立,通知等待线程 66 pthread_cond_signal(&g_cond); 67 printf("%d signal \n",num);//某个线程发起通知 68 pthread_mutex_unlock(&g_mutex); 69 sleep(1); 70 } 71 return NULL; 72 } 73 int main(void) 74 { 75 int i; 76 //初始化互斥量和条件变量 77 pthread_mutex_init(&g_mutex,NULL); 78 pthread_cond_init(&g_cond,NULL); 79 //创建消费者线程 80 for(i=0;i<CONSUMERS_COUNT;i++) 81 { 82 pthread_create(&g_thread[i],NULL,consume,(void*)i);//产生了段错误.我们传递的是值,所以int num=(int)arg 而不是int num=*(int*)arg 83 } 84 85 sleep(1);//以便消费者进入等待 86 //创建生产者线程 87 for(i=0;i<PRODUCERS_COUNT;i++) 88 { 89 pthread_create(&g_thread[i+CONSUMERS_COUNT],NULL,produce,(void*)i); 90 } 91 //等待线程退出 92 for(i=0;i<CONSUMERS_COUNT+PRODUCERS_COUNT;i++) 93 { 94 pthread_join(g_thread[i],NULL); 95 } 96 97 pthread_cond_destroy(&g_cond); 98 pthread_mutex_destroy(&g_mutex); 99 return 0; 100 }