http://www.ibm.com/developerworks/cn/linux/thread/posix_thread3/#1
当多线程想要等待某一条件成立时,用pthread_cond_wait来阻塞线程,
- 首先明确wait的作用,pthread的wait与lock都可以阻塞线程等待“条件”成立,区别在于:
wait之后的发送来的条件成立(pthread_cond_broadcast())才有效,之前发生都被丢弃了,也就是说wait的首次调用肯定会阻塞线程,不会因条件成立过而向下运行;
而lock的条件在成立(信号量未被别人lock )后会一直维持 unlock的状态,那么线程首次调用lock就不一定会阻塞,而取决于“条件”状态。
- 那么问题来了,为什么pthread_cond_wait调用之前必须保证本线程已经被lock了?为什么wait要被设计成这样?
据说是因为wait时如果有条件达成,会因为wait未能及时加入到队列中而错失这个条件,只是这样吗?
试一下就知道了,本线程未被lock时调用wait也是可以的,wait也能正常工作啊。
下面用测试代码来说明这个问题:
void * foo1(void * p){ while(1){ printf("foo1 wait begin\n"); pthread_cond_wait(&c,&m); printf("foo1 wait end\n"); sleep(1); } } void * foo2(void * p){ while(1){ printf("foo2 wait begin\n"); pthread_cond_wait(&c,&m); printf("foo2 wait end\n"); sleep(1); } } void * foo3(void * p){ while(1){ getchar(); pthread_cond_broadcast(&c); } } int pthreadTest(){ pthread_t tid[10]; pthread_create(tid+2,NULL,foo1,NULL); pthread_create(tid+1,NULL,foo2,NULL); pthread_create(tid+3,NULL,foo3,NULL); pthread_join(*(tid+1),NULL); pthread_join(*(tid+2),NULL); }结果
foo2 wait end
foo2 wait begin
foo1 wait end
foo1 wait begin
foo2 wait end
foo2 wait begin
foo1 wait end
foo1 wait begin
从结果上看 wait唤醒的时候会lock住mutex,当线程循环一遍后再次调用wait而释放mutex时 再唤醒其他线程。
那么,线程间使用不同的mutex是否就可以同时唤醒两个线程的wait了呢,也就是是否就能够单纯的用cond控制wait唤醒?每个线程一个mutex,这样就不会被其他线程锁住自己的信号量了吧,下面测试一下:
void * foo1(void * p){ while(1){ printf("foo1 wait begin\n"); pthread_cond_wait(&c,&m11111); printf("foo1 wait end\n"); sleep(1); } } void * foo2(void * p){ while(1){ printf("foo2 wait begin\n"); pthread_cond_wait(&c,&m22222); printf("foo2 wait end\n"); sleep(1); } }结果:
foo2 wait end
foo2 wait begin
foo2 wait end
foo2 wait begin
这?????,只唤醒了一个线程(具体唤醒哪个线程取决于哪个线程先执行),pthread_cond_broadcast声称激活所有等待线程,但看来并不简单,可能是wait的时候同一个cond是事实上他的激活方式是串行的(也就是每次的pthread_cond_signal后根据wait的情况决定下一次是否signal),另一种可能是后运行的线程的mutex出现了问题。
那么将pthread_cond_broadcast改成pthread_cond_signal后测试看看,结果:
foo1 wait end
foo1 wait begin
foo2 wait end
foo2 wait begin
foo1 wait end
foo1 wait begin
foo2 wait end
foo2 wait begin
这是否说明了与mutex无关而激活方式确实是串行的呢?再试试每次pthread_cond_broadcast后调用unlock(&m11111),结果:
foo1 wait end
foo2 wait end
foo1 wait begin
foo2 wait begin
foo1 wait end
foo2 wait end
foo1 wait begin
foo2 wait begin
恩,看来应该是后运行的线程在wait唤醒时无法lock自己的mutex,但是根据刚才signal的结果看,这又是谁lock了它的mutex呢?
经过思考,终于把自己搞晕了......一个问题如果不能理解,那就会产生宗教,比如:“别问为什么,书上就是这么说的!”
所以 真正的原因就是:一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据“内部”的一种特殊的条件更改。一个互斥对象可以用许多条件变量(例如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。
同时,wait也只有一个正确用法:如果线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值。但这是在浪费时间和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用 pthread_cond_wait() 调用来等待特殊条件发生。
所以我们记住这个结论:照着他这么用就得了......否则只会走火入魔