- 论坛徽章:
- 44
|
本帖最后由 windoze 于 2014-08-22 17:39 编辑
回复 26# gaojl0728
举个无脑的例子:
假定有个计数器线程每次把计数器加一,有个监视线程,一个看计数器是不是10的倍数
那么监视线程你会这么写:
- pthread_mutex_lock(mtx);
- while(1) {
- pthread_cond_wait(cv, mtx);
- if(!(count%10)) { /* do something */ }
- }
- pthread_mutex_unlock(mtx);
复制代码 计数器线程你可能会这么写:
- count++;
- pthread_cond_signal(cv, mtx);
复制代码 你要问为啥不在这里检查条件?因为不方便啊,要是我有天想要看20的倍数,我只需要改一处就好了,如果在这里也检查条件我需要改两处,还有可能会漏一个。
当然这样有race condition对不对?写count没有与读count互斥,所以你可能会这样:
- pthread_mutex_lock(mtx);
- count++;
- pthread_mutex_unlock(mtx);
- pthread_cond_signal(cv, mtx);
复制代码 这样count的race condition没有了,但是你仔细想想,在监视线程中:
- ...
- // 假如此时count==10,监视线程调用了signal
- pthread_cond_wait(cv, mtx);
- // <---假如此时计数器线程又执行了一次,count变成了11,信号就丢了
- if(!(count%10)) { /* do something */ }
- ...
复制代码 要想防止这种现象发生,你必须保证在上面的这个点决不会执行计数器线程。
因为cond_wait返回后就自动获得了锁,所以你可以用这一点进行互斥,让计数器线程一直等到下次进入pthread_cond_wait,此时锁自动释放了。
所以你需要把计数器线程写成这样:
- pthread_mutex_lock(mtx);
- count++;
- pthread_cond_signal(cv, mtx);
- pthread_mutex_unlock(mtx);
复制代码 把signal放在锁里面,这样就可以保证不会丢失信号。
**条件和实际检查的条件不一致就会造成所谓的“spurious wakeup”,在这里计数器每次有变化的时候都会**等待线程,但等待线程实际上要一直等到计数器变为10的倍数,这一部分是你自己的程序逻辑,pthread的实现没法帮你。
凡是会有假**的情况,你必须保证pthread_cond_signal不是naked,也就是说必须包在lock里,否则就有可能丢信号。 |
|