1. 关于pthread_cond系列函数
pthread_cond_wait的参数里有一个mutex,其作用往往令人费解。
最近在考察OpenMAX系统发现这一部分的实现可能导致较大的风险。
一般而言有了这个mutex以后对于cond实现一些增强的特性就比较方便。
例如,如果要将cond用作semaphore,以下代码应该能起作用:
- sem_down(sem)
- {
- pthread_mutex_lock(sem->mutex);
- // critical section
- sem->count--;
- if (sem->count < 0)
- {
- pthread_cond_wait(sem->cond, sem->mutex); // the mutex is unlocked once the thread gets blocked
- // mutex is locked and owned by the current thread again after it is scheduled to run when unblocked
- }
- pthread_mutex_unlock(sem->mutex)
- }
- sem_up(sem)
- {
- pthread_mutex_lock(sem->mutex); // absence of this mutex acquisition may cause severe error
- sem->count++;
- pthread_cond_signal(sem->cond);
- pthread_mutex_unlock(sem->mutex)
- }
这样cond和count的操作被完全保护(原子化),同时由于pthread_cond_wait在cond阻塞情况下自动释放mutex又不至于导致死锁。
但是其前提条件是底层的OS正确地实现这几个POSIX接口的意图(这不是必然的)。否则需要通过时序的分析确定可能造成的问题。
注意在sem_up中如果不做互斥量保护则在如下情况下发生错误:
线程A阻塞在pthread_cond_wait,sem->count计数为-1;
线程B进入sem_down,并执行完成sem->count--后,调度到线程C;
线程C进入sem_up,执行sem->count++,和pthread_cond_signal,使得sem->count计数变为0,同时线程A被激活;
这样线程A可获得mutex并从sem_down返回;
线程B条件判断不成立不被阻塞,亦返回。但显然在这个序列中应该至少有一个线程被阻塞。(按理想顺序应该B被阻塞)
由于posix规定中关于pthread_cond_signal触发的说明是至少释放一个阻塞,于是存在同时释放多个阻塞的情形。如下代码也许可以解决这个问题:
- sem_down(sem)
- {
- pthread_mutex_lock(sem->mutex);
- // critical section
- sem->count--;
- if (sem->count < 0)
- {
- _again:
- sem->nblocked++; // count the number of blocked threads
- pthread_cond_wait(sem->cond, sem->mutex);
- sem->nblocked--;
- if (sem->nblocked + sem->count < 0)
- { // this thread is not ready to be unblocked
- goto _again;
- }
- }
- pthread_mutex_unlock(sem->mutex);
- }
如果count不记录被阻塞的线程个数,以下方案很理想:
- sem_down(sem)
- {
- pthread_mutex_lock(sem->mutex);
- // critical section
- while(sem->count == 0)
- {
- pthread_cond_wait(sem->cond, sem->mutex);
- }
- sem->count--;
- pthread_mutex_unlock(sem->mutex);
- }