POSIX条件变量

条件变量:
  当一个线程互斥的访问某个变量时,它可能发现其他线程改变状态之前,它什么都做不了
例如:一个线程访问队列时,发现队列为空,它只能等待,直到其他线程将一个节点添加到队列中,这种情况就需要使用条件变量。

线程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 }

 

posted on 2018-03-03 20:01  wsw_seu  阅读(306)  评论(0编辑  收藏  举报

导航