pthread多线程学习笔记五条件变量2使用
上篇笔记的例子运行效果没有问题,但有些疑问还没搞清楚。
wait函数并不是单使该线程进入休眠。
ß wait函数做以下三步的操作:
ß 1.释放Mutex 2.阻塞等待(不耗费cpu周期) 3.当被唤醒时,重新锁定互斥锁并重新测试条件是否满足 。
ß 需要重新测试条件的原因是因为可能存在多个consumer的情况,即如果线程不止两个而是多个,即使线程被唤醒了,如果一个consumer取走了列表里的产品,那么另外一个consumer需要重新进入休眠等待。这也是为什么用while不用if去判断的原因。条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出。从wait或者timewait调用成功返回时,线程需要重新计算条件,因为其他的线程可能已经在运行并改变了条件。
wait函数会在 休眠等待之前释放锁,因此producer是不用担心一直获取不到锁的。
传递给wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。这就是互斥量和条件一起使用的原因,如果consumer判断到列表为空,正准备进入休眠,而此时producer产生节点放到了列表里,consumer却接着进入休眠很明显不是我们想要的,如果producer在其休眠前发出了信号,又会造成没有接受者的情形(一会会讲到这个)。wait返回时,互斥量再次被锁住。
要注意,一定要在改变条件之后再给线程发信号。否则无意义的唤醒休眠线程就违反我们最初引入条件变量的目的了
关于signal与mutex的相对位置,《UNIX高级环境编程》里是说都可以的,只要在cond_signal之后能从列表里取出节点。因为是while循环检查条件,所以不会存在问题:线程醒来,发现队列为空,然后返回继续等待。如果代码不能容忍这种竞争,就需要在向线程发送信号的时候占有互斥量。
关于这个,从网上找了下资料,也是各执一词,主要是跟当前系统环境有关系,因此并无定论。下面的这个感觉是比较合理的:
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的线程),而这在上面的放中间的模式下是不会出现的。
所以,在Linux下最好pthread_cond_signal放中间,但从编程规则上说,其他两种都可以。
我理解为signal后唤醒的线程会具有较高的加锁优先级,如果有多个线程在等待给该互斥量加锁的话,唤醒线程会获取得到锁,而这在大部分情况下都是我们所希望看到的。当然如文章中所言,可能会有性能的问题。
关于signal与broadcast的区别,找到一个解释很有意思(如果我的确看明白了的话):
FiLH wrote On 03/29/06 08:47,:
Hello,
I wonder what is the effective usage difference between
pthread_cond_signal and pthread_cond_broadcast.
I understand that the first awake one waiting thread, and the second
all the waiting threads (on that condition),
That is the difference.
but be seen that the
awkaed thread should enter a mutex, it seems to me in the second case
that only one thread will run while the other one will go back
waiting. But than, I see no big differences, so I must miss something,
but.. what ?
All the threads wake up, and all try to lock the
mutex. Only one at a time can succeed, and the others
must wait for a later opportunity. Yes, they "go back
waiting" -- but they are not waiting for the condition
any more, they are waiting to lock the mutex.
The bugler sounds Reveille, all the soldiers in the
barracks awaken, and they all rush for the lone shower
stall. One soldier gets there first and showers, while
the others stand around fidgeting. When the first is
done, there's a brief struggle among the others until
a second soldier hits the shower, and so on until all
the soldiers have made themselves nice and clean and
ready to go crawl in the mud all day. The soldiers who
don't happen to be first into the shower don't all go
back to bed and await another bugle call (they might
wish to, but the Drill Sergeant has other ideas).
ok。可能关于一些问题,更多的还需要具体情况具体分析。