pthread_mutex_t & pthread_cond_t 总结( 五 )

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中这样用没问题 。
4.3.2 写法二{    pthread_mutex_lock(&mutex);    // ToDo    pthread_mutex_unlock(&mutex);    pthread_cond_signal(&cond);}优点:不会出现之前说的那个潜在的性能损耗,因为在 signal 之前就已经释放锁了
缺点:如果 unlock 之后 signal 之前,发生进程交换,另一个进程(不是等待条件的进程)拿到这把梦寐以求的锁后加锁操作 , 那么等最终切换到等待条件的线程时锁被别人拿去还没归还,只能继续等待 。
参考资料
  • linux线程互斥量pthread_mutex_t使用简介
  • pthread_mutex_t 互斥锁 - 简书 (jianshu.com)
  • C语言中pthread_cond_wait 详解
  • Linux中,pthread_cond_t, 条件变量的使用举例
  • linux进阶52——pthread_cond_t
  • 关键字 - restrict
  • *restrict 功能
【pthread_mutex_t & pthread_cond_t 总结】

推荐阅读