pthread_mutex_t & pthread_cond_t 总结( 六 )


结合下面的伪代码来加深理解:
/* 线程A执行函数 */int giFlag = FALSE; // FALSE:线程A不满足执行条件void *funcA(void *arg){    // pthread_mutex_lock(&mutex); // 传入前不加锁    while (FALSE == giFlag)    {        // 在调用pthread_cond_wait前,线程B启动并执行了函数funcB,修改条件并发出信号        // 但此时由于线程A还未执行pthread_cond_wait函数,所以会忽略掉线程B发出的信号        // 等到线程A开始执行pthread_cond_wait时,已经收不到来自线程B的信号了,会一直阻塞        pthread_cond_wait(&cond, &mutex);    }    // ToDo    pthread_mutex_unlock(&mutex);    return NULL;}/* 线程B执行条件 */void *funcB(void *arg){    pthread_mutex_lock(&mutex);    // 线程B将giFlag置为TRUE,并通过cond_signal将信号发送给了线程A    giFlag = TRUE;    pthread_cond_signal(&cond);    pthread_mutex_unlock(&mutex);    return NULL;}4.2.2 传入后为何要释放

传入后解锁是为了条件能够被改变 。
传入后的解锁,是因为调用 pthread_cond_signal 的那部分,需要先加锁更改条件后才调用 pthread_cond_signal(更改条件与等待条件满足,都是针对条件这一个资源的竞争,所以调用 pthread_cond_wait 和调用 pthread_cond_signal 的两个线程需要同一把锁) 。
如果 pthread_cond_wait 内不对 mutex 解锁,那么在调用 pthread_cond_wait 后,其他线程就不能更改条件,条件就会一直不满足 。
4.2.3 返回时又为何再次锁
  1. 返回前再次锁 mutex 是为了保证条件从「线程从 pthread_cond_wait 返回后」到「再次条件判断前」不被改变 。
  2. 使得在「pthread_cond_signal之后」与「pthread_mutex_unlock 之前」可以执行其他的语句 。
对于 1,这里的理由与传入 pthread_cond_wait 前锁 mutex 的理由差不多 。如果不锁,那么线程 A 调用 pthread_cond_wait后,条件满足,线程 A 被唤醒,从 pthread_cond_wait 返回 。线程 B 在此时更改了条件,使得条件不满足,线程 A 并不知道条件又被更改,还是以为条件满足,就可能出错 。
对于 2,由于 mutex 在这时已经被这个线程锁住,还没有解锁,所以调用 pthread_cond_wait 的那个线程在 pthread_cond_wait 返回前的锁 mutex 的行为就会阻塞,直到 pthread_cond_signal 后的语句执行完并解锁,pthread_cond_wait 才会返回 。
4.3 pthread_cond_signal 的两种写法由于 pthread_cond_wait 返回前再次锁的行为,所以 pthread_cond_signal 不一定必须放在解锁 mutex之前 。
4.3.1 写法一{    pthread_mutex_lock(&mutex);    // ToDo    pthread_cond_signal(&cond);    // ToDo    pthread_mutex_unlock(&mutex);}缺点:在某些线程的实现中,会造成等待线程从内核中被唤醒(接收到了 cond_signal 发出的信号)回到用户空间,然后 pthread_cond_wait 返回前需要加锁,但是发现锁没有被释放,又回到内核空间所以一来一回会有性能的问题 。
但是在 LinuxThreads 或者 NPTL 里面,就不会有这个问题 。因为在 Linux 线程中,有两个队列,分别是 cond_wait 队列和mutex_lock 队列,cond_signal 只是让线程从 cond_wait 队列移到 mutex_lock 队列,而不用返回到用户空间,不会有性能的损耗,所以Linux中这样用没问题 。

经验总结扩展阅读