互斥锁与条件变量

来源:互联网 发布:周生生淘宝旗舰店 编辑:程序博客网 时间:2024/05/02 00:54
再谈互斥锁与条件变量!(终于搞清楚了啊!!!!!) 

【转自】http://blog.chinaunix.net/uid-27164517-id-3282242.html

pthread_cond_wait总和一个互斥锁结合使用。在调用pthread_cond_wait前要先获取锁pthread_cond_wait函数执行时先自动释放指定的锁,然后等待条件变量的变化。在函数调用返回之前,自动将指定的互斥量重新锁住

int pthread_cond_signal(pthread_cond_t * cond);

pthread_cond_signal通过条件变量cond发送消息,若多个消息在等待,它只唤醒一个。pthread_cond_broadcast可以唤醒所有。调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。


无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race   Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁 (PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁 (pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开 pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。  
   
  激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。

下面是另一处说明:给出了函数运行全过程。 为什么在唤醒线程后要重新mutex加锁?

了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。

首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能:

锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。 【要求解锁并阻塞是一个原子操作

此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。

现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒

现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。


来看一个例子(你是否能理解呢?):

 

In Thread1:

pthread_mutex_lock(&m_mutex);   
pthread_cond_wait(&m_cond,&m_mutex);   
pthread_mutex_unlock(&m_mutex);  

 

In Thread2:

pthread_mutex_lock(&m_mutex);   
pthread_cond_signal(&m_cond);   
pthread_mutex_unlock(&m_mutex);  

 

为什么要与pthread_mutex 一起使用呢? 这是为了应对 线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候,此时线程2调用了 cond_singal 的情况。 如果不用mutex锁的话,这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex 被释放(也就是 pthread_cod_wait() 释放锁并进入wait_cond状态 ,此时线程2上锁) 的时候才能调用cond_singal.


pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有有缺点。

之间:
pthread_mutex_lock
    xxxxxxx
pthread_cond_signal
pthread_mutex_unlock
缺点:在某下线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的 行为),所以一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。
所以在Linux中推荐使用这种模式。

之后:
pthread_mutex_lock
    xxxxxxx
pthread_mutex_unlock
pthread_cond_signal
优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了
缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。


======================================================================
【转自】http://blog.csdn.net/maximuszhou/article/details/42318169
浅析线程间通信一:互斥量和条件变量
线程同步的目的简单来讲就是保证数据的一致性。在Linux中,常用的线程同步方法有互斥量( mutex )、读写锁和条件变量,合理使用这三种方法可以保证数据的一致性,但值得的注意的是,在设计应用程序时,所有的线程都必须遵守相同的数据访问规则为前提,才能保证这些同步方法有效,如果允许某个线程在没有得到访问权限(比如锁)的情况下访问共享资源,那么其他线程在使用共享资源前都获得了锁,也会出现数据不一致的问题。另外还有自旋锁、barrier和信号量线程同步方法。本文将讨论互斥量和条件变量的使用,并给出了相应的代码和注意事项,相关代码也在我的github上下载。

    互斥量

    互斥量从本质上说是一把锁,在访问共享资源前主动对互斥量进行加锁,在访问完成后需要主动释放互斥量上的锁。对互斥量加锁后,其他线程试图对互斥量加锁时都会被阻塞直到互斥量的锁释放(注意如果线程试图对同一个互斥量加锁两次,那么它自身就会陷入阻塞,即死锁状态。)。如果释放互斥量锁时有多个线程阻塞,所有阻塞在该互斥量的线程都会变成可运行状态,其中第一个变成可运行状态的线程获得互斥量的锁,其他线程会再次阻塞(注意那个阻塞线程的获得锁是跟实现相关的,是不确定的,比如可能是首先唤醒优先级最高的被阻塞线程,而优先级相同的,则按FIFO原则来唤醒。)下面是使用互斥量来处理经典的生产者-消费者问题,代码如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <pthread.h>  
  4.   
  5. #define MAXNITEMS       1000000  
  6. #define MAXNTHREADS         100  
  7. #define MIN(a,b) (((a) < (b))?(a):(b))  
  8.   
  9. int     nitems;         /* read-only by producer and consumer,to express maximum item */  
  10. struct {  
  11.   pthread_mutex_t   mutex;  
  12.   int   buff[MAXNITEMS];  
  13.   int   nput;  
  14.   int   nval;  
  15. } shared = { PTHREAD_MUTEX_INITIALIZER };  
  16.   
  17. void    *produce(void *), *consume(void *);  
  18.   
  19. int main(int argc, char **argv)  
  20. {  
  21.     int         i, nthreads, count[MAXNTHREADS];  
  22.     pthread_t   tid_produce[MAXNTHREADS], tid_consume;  
  23.   
  24.     if (argc != 3)  
  25.     {  
  26.         printf("usage: prodcons4 <#items> <#threads>\n");  
  27.         return 1;  
  28.     }  
  29.     nitems = MIN(atoi(argv[1]), MAXNITEMS);  
  30.     nthreads = MIN(atoi(argv[2]), MAXNTHREADS);  
  31.   
  32.     printf("main:%d,%d,%d",shared.nput,shared.nval,shared.buff[0]);  
  33.   
  34.     /* create all producers and one consumer */  
  35.     for (i = 0; i < nthreads; i++) {  
  36.         count[i] = 0;  
  37.         pthread_create(&tid_produce[i], NULL, produce, &count[i]);  
  38.     }     
  39.     pthread_create(&tid_consume, NULL, consume, NULL);  
  40.   
  41.     /* wait for all producers and the consumer */  
  42.     for (i = 0; i < nthreads; i++) {  
  43.         pthread_join(tid_produce[i], NULL);  
  44.         printf("count[%d] = %d\n", i, count[i]);          
  45. }  
  46.     pthread_join(tid_consume, NULL);  
  47.   
  48.     exit(0);  
  49. }  
  50.   
  51. void * produce(void *arg)  
  52. {  
  53.     for ( ; ; ) {  
  54.         pthread_mutex_lock(&shared.mutex);  
  55.         if (shared.nput >= nitems) {  
  56.             pthread_mutex_unlock(&shared.mutex);  
  57.             return(NULL);       /* array is full, we're done */  
  58.         }  
  59.         shared.buff[shared.nput] = shared.nval;  
  60.         shared.nput++;  
  61.         shared.nval++;  
  62.         pthread_mutex_unlock(&shared.mutex);  
  63.         *((int *) arg) += 1;  
  64.     }  
  65. }  
  66.   
  67. void consume_wait(int i)  
  68. {  
  69.     for ( ; ; ) {  
  70.         pthread_mutex_lock(&shared.mutex);  
  71.         if (i < shared.nput) {  
  72.             pthread_mutex_unlock(&shared.mutex);  
  73.             return;         /* an item is ready */  
  74.         }  
  75.         pthread_mutex_unlock(&shared.mutex);  
  76.         sched_yield();  
  77.     }  
  78. }  
  79.   
  80. void * consume(void *arg)  
  81. {  
  82.     int     i;  
  83.   
  84.     for (i = 0; i < nitems; i++) {  
  85.         consume_wait(i);  
  86.         if (shared.buff[i] != i)  
  87.             printf("buff[%d] = %d\n", i, shared.buff[i]);  
  88.     }  
  89.     return(NULL);  
  90. }  

编译运行后结果如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. $gcc -Wall -lpthread mutex_example.c -o mutex_example  
  2. $./mutex_example 1000000 5  
  3. count[0] = 188090  
  4. count[1] = 197868  
  5. count[2] = 194924  
  6. count[3] = 211562  
  7. count[4] = 207556  

上面程序实现是多个生产者线程和一个消费者线程同步,有以下几个地方值得注意:

I)结构体shared中包括一个互斥变量和需要同步的数据,把他们封装到一个结构体的目的是为了强调这些变量只应该在拥有其互斥锁时访问。把共享数据和它们的同步变量(互斥锁、条件变量或信号量)封装到一个结构体中,是一个很好的编程技巧。

II)在函数produce()中,每个线程的count元素的增加不属于锁的临界区,因为每个线程由各自的计数器count[i],应该总是尽可能减少由一个互斥锁锁住的代码量。

III)在函数consume()中,调用了函数consume_wait(i)来检测期待的条目是否准备好了,若没有准备好,则调用sched_yield(),调用该接口后,会使得消费线程让出当前的CPU,并把该线程移到相应优先级队列的末尾。在这里是消费线程把CPU让出给生产者线程。但这仍然不是理想的方法,理想的方法,当buff中有数据时(或有指定数目的数据时),生产者线程唤醒消费者线程。

    条件变量

     条件变量是线程可用的另一种同步机制。互斥量用于上锁,条件变量则用于等待,并且条件变量总是需要与互斥量一起使用。下面是用条件变量重新实现上面的消费者-生产者问题,代码如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <pthread.h>  
  4.   
  5. #define MAXNITEMS       1000000  
  6. #define MAXNTHREADS         100  
  7. #define MIN(a,b) (((a) < (b))?(a):(b))  
  8.   
  9. int     nitems;             /* read-only by producer and consumer,to express maximum item */  
  10. int     buff[MAXNITEMS];  
  11. struct {  
  12.   pthread_mutex_t   mutex;  
  13.   int               nput;   /* next index to store */  
  14.   int               nval;   /* next value to store */  
  15. } put = { PTHREAD_MUTEX_INITIALIZER };  
  16.   
  17. struct {  
  18.   pthread_mutex_t   mutex;  
  19.   pthread_cond_t    cond;  
  20.   int               nready; /* number ready for consumer */  
  21. } nready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER };  
  22.   
  23. int     nsignals; /*call pthread_cond_signal count*/  
  24.   
  25. void    *produce(void *), *consume(void *);   
  26.   
  27. int main(int argc, char **argv)  
  28. {  
  29.     int         i, nthreads, count[MAXNTHREADS];  
  30.     pthread_t   tid_produce[MAXNTHREADS], tid_consume;  
  31.   
  32.     if (argc != 3)  
  33.     {     
  34.         printf("usage: prodcons4 <#items> <#threads>\n");  
  35.         return 1;  
  36.     }     
  37.     nitems = MIN(atoi(argv[1]), MAXNITEMS);  
  38.     nthreads = MIN(atoi(argv[2]), MAXNTHREADS);  
  39.   
  40.     /* create all producers and one consumer */  
  41. for (i = 0; i < nthreads; i++) {  
  42.         count[i] = 0;  
  43.         pthread_create(&tid_produce[i], NULL, produce, &count[i]);  
  44.     }  
  45.     pthread_create(&tid_consume, NULL, consume, NULL);  
  46.   
  47.     /* wait for all producers and the consumer */  
  48.     for (i = 0; i < nthreads; i++) {  
  49.         pthread_join(tid_produce[i], NULL);  
  50.         printf("count[%d] = %d\n", i, count[i]);  
  51.     }  
  52.     pthread_join(tid_consume, NULL);  
  53.     printf("nsignals = %d\n", nsignals);  
  54.   
  55.     exit(0);  
  56. }  
  57.   
  58. void * produce(void *arg)  
  59. {  
  60.     for ( ; ; ) {  
  61.         pthread_mutex_lock(&put.mutex);  
  62.         if (put.nput >= nitems) {  
  63.             pthread_mutex_unlock(&put.mutex);  
  64.             return(NULL);       /* array is full, we're done */  
  65.         }  
  66.         buff[put.nput] = put.nval;  
  67.         put.nput++;  
  68.         put.nval++;  
  69.         pthread_mutex_unlock(&put.mutex);  
  70.   
  71.         pthread_mutex_lock(&nready.mutex);  
  72.         if (nready.nready == 0) {  
  73.             pthread_cond_signal(&nready.cond);  
  74.             nsignals++;  
  75.         }  
  76.         nready.nready++;  
  77.         pthread_mutex_unlock(&nready.mutex);  
  78.   
  79.         *((int *) arg) += 1;  
  80.     }  
  81. }  
  82.   
  83. void * consume(void *arg)  
  84. {  
  85.     int     i;  
  86.   
  87.     for (i = 0; i < nitems; i++) {  
  88.         pthread_mutex_lock(&nready.mutex);  
  89.         while (nready.nready == 0)  
  90.             pthread_cond_wait(&nready.cond, &nready.mutex);  
  91.         nready.nready--;  
  92.         pthread_mutex_unlock(&nready.mutex);  
  93.   
  94.         if (buff[i] != i)  
  95.             printf("buff[%d] = %d\n", i, buff[i]);  
  96.     }  
  97.     return(NULL);  
  98. }  

编译运行后结果如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. $gcc -Wall -lpthread cond_var_example.c  -o cond_var_example  
  2. $./cond_var_example  1000000 5  
  3. count[0] = 220234  
  4. count[1] = 201652  
  5. count[2] = 199165  
  6. count[3] = 182972  
  7. count[4] = 195977  
  8. nsignals = 3  

上面程序实现是多个生产者线程和一个消费者线程同步,有以下几个地方值得注意:

      I)因为调度的不确定性,程序运行的结果每次都是不一样的。

      II)在consume()中可以看到,消费线程通过一个while循环等待nready.nready(用来统计当前有多少条目供消费者处理)变为非0,若nready.nready值为0,则调用pthread_cond_wait()阻塞线程,使线程进入休眠状态,直到其他线程调用pthread_cond_signal()唤醒它,而在唤醒它之前,该线程一直处于休眠状态,并不会参与调度或消耗CPU。关于函数pthread_cond_wait(),需要注意以下几点:

      a、线程在调用pthread_cond_wait()函数之前,需要获得与之关联的互斥量后才能调用改接口,在上面的例子中,就是计数器nready.nready对应的互斥量nready.mutex,只是获得改互斥量后,线程才能判断nready.nready是否为零,若是,进而以刚才互斥量的地址作为参数调用pthread_cond_wait()接口。若没有获取与之关联的互斥量就调用pthread_cond_wait,其结果是未定义的。

      b、调用pthread_cond_wait()函数,该函数原子地执行下面两个动作:首先给互斥量nready.mutex解锁;然后把调用线程投入睡眠,直到另外某个线程就本条件变量调用pthread_cond_signal。注意这两个操作必须是原子操作,否则线程在解锁后,投入睡眠之前,其他线程调用pthread_cond_signal的话,当前线程投入睡眠后,就一直得不到通知了,即错过条件的变化,在这里是nready.nready的值变化。执行上面两个操作后,线程已经睡眠了,此时函数pthread_cond_wait()还没有返回。

      c、当其他线程调用pthread_cond_signal或pthread_cond_broadcast时,会唤醒相应条件变量等待的线程,此时被唤醒的线程,可以参与调度了,此时被唤醒的线程继续执行pthread_cond_wait()函数,函数pthread_cond_wait()返回之前,会重新给条件变量对应的互斥量上锁,在这里就是nready.mutex,若该函数成功返回,则当前线程有重新获得了nready.mutex锁,当然nready.mutex也可能被其他线程继续占有,此时线程再次阻塞。总之,在函数pthread_cond_wait()成功返回之前,必然又对相应的互斥量成功加锁了,pthread_cond_wait从调用到返回,整个过程相当于:unlock, just_wait, lock这三个操作,并且前面两个操作是原子操作。

      d、在pthread_cond_wait()成功返回后,需要重新检查对应的条件是否成立,在这里是nready.nready的值。因为可能发生虚假的唤醒:期待的条件(在这里是消费线程期待nready.nready的值不为0)还不成立时的唤醒。各种线程实现都应该最大限度减少这些虚假唤醒的数量。

      III)在produce()可以看到,在nready.nready的值加1之前,如果计数器的值为0,那就调用pthread_cond_signal唤醒可能正在等待其值变为非零的线程。可以看出,互斥量是用来同步对nready.nready的访问,而关联的条件变量则用于等待和发送信号。如果仅仅是使用互斥量来同步,则就可能出现前面的忙等待(即使调用sched_yield也只是延迟了而已),而使用条件变量后,则线程就可以阻塞休眠了,直到需要的条件发生(在这里就是nready.nready的值非零),而改变这个条件的线程也是通过条件变量(以条件变量做为参数调用pthread_cond_signal或pthread_cond_broadcast)来通知阻塞在相应条件变量的线程。

      IV)调用pthread_cond_signal函数前不是一定要对互斥变量加锁的(当然也不存在返回时释放互斥量的情况),但通常修改条件变量时需要加锁,这就导致一个问题。pthread_cond_signal即可以放在pthread_mutex_lockpthread_mutex_unlock之间,也可以放在 pthread_mutex_lockpthread_mutex_unlock之后,到底放在什么位置比较好。在上面的例子中,使用的的属于第一种方法。即下面的形式:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. pthread_mutex_lock  
  2. xxxxxxx  
  3. pthread_cond_signal  
  4. pthread_mutex_unlock  
这样做的缺点是:在最话情况下,当该条件变量被发送信号后,系统立即调度等待其上的线程,该线程开始运行,但是在pthread_cond_wait返回之前,需要再次获得互斥量,而此时互斥量被其他线程(即唤醒它的那个线程)占有,因此被唤醒的线程再次阻塞,所以这样一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, pthread_cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。另外一种情况形式如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. pthread_mutex_lock  
  2. xxxxxxx  
  3. pthread_mutex_unlock  
  4. pthread_cond_signal  

而我们上面的例子对应代码,可以改成:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int dosignal;  
  2. pthread_mutex_lock(&nready.mutex);  
  3. dosignal = (nready.nready == 0);  
  4. pthread_mutex_unlock(&nready.mutex);  
  5. if (dosignal)  
  6. pthread_cond_signal(&nready.cond);  
这种形式优点是不会出现之前说的那个潜在的性能损耗,因为在通知等待线程之前就已经释放锁了;缺点是如果unlockpthread_cond_signal之间,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程 (调用pthread_cond_wait的线程),而这在上面的放中间的模式下是不会出现的。

      V)通常pthread_cond_signal函数只唤醒等待在相应条件变量上的一个线程。如果没有线程处在阻塞等待在相应条件变量,pthread_cond_signal也会成功返回。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次(按照POSIX规定是至少一个线程唤醒,该接口实现也可能唤醒多个线程)。而在某些情况下,可能需要唤醒多个线程(比如一个写入者唤醒多个读者线程),此时可以调用pthread_cond_broadcast唤醒阻塞在相应条件变量上的所有线程。

      VI)总的来说,给条件变量发送信号的代码大体如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. struct {      
  2.   pthread_mutex_t   mutex;  
  3.   pthread_cond_t    cond;  
  4.   维护本条件的各个变量  
  5.   } var= { PTHREAD_MUTEX_INITIALIZER, \  
  6. PTHREAD_COND_INITIALIZER };  
  7. pthread_mutex_lock(&var.mutex);   
  8. 设置条件为真  
  9. pthread_cond_signal(&var.cond);  
  10. pthread_mutex_unlock(&var.mutex);  
在我们的例子中,用来维护条件的变量是一个整数计数器,设置条件的操作就是给该计数器加1。我们做了优化处理,即只有该计数器从0变为1时才发出条件变量的信号。测试条件并进入睡眠以等待条件变量为真的代码大体如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. pthread_mutex_lock(&var.mutex);  
  2. while (条件为假)  
  3. pthread_cond_wait(&var.cond, &var.mutex);  
  4. 修改条件  
  5. pthread_mutex_unlock(&var.mutex);  
      VI)为什么使用条件变量,需要对一个互斥量加锁?因为条件变量的实现,是以已经获得的加锁的互斥量为前提的。若没有获取与之关联的互斥量就调用pthread_cond_wait,其结果是未定义的。这个互斥量就是用来对保护条件本身的访问。

参考资料

《UNIX环境高级编程》 11.6线程的同步 
《UNIX网络编程卷2:进程间通信》第7章、第8章和第10章
http://stackoverflow.com/questions/2763714/why-do-pthreads-condition-variable-functions-require-a-mutex 

0 0
原创粉丝点击