为什么线程同步的时候pthread_cond_t要和pthread_mutex_t同时使用
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count = 0;
decrement_count () {
pthread_mutex_lock (&count_lock);
while(count==0)
pthread_cond_wait( &count_nonzero, &count_lock);
count=count -1;
pthread_mutex_unlock (&count_lock);
}
increment_count(){
pthread_mutex_lock(&count_lock);
if(count==0)
pthread_cond_signal(&count_nonzero);
count=count+1;
pthread_mutex_unlock(&count_lock);
}
decrement_count和increment_count在两个线程A和B中被调用。
正确的情况下,如果decrement_count首先运行,那么A会被阻塞到pthread_cond_wait。随后increment_count运行,它调用pthread_cond_signal唤醒等待条件锁count_nonzero的A线程,但是A线程并不会马上执行,因为它得不到互斥锁count_lock。当B线程执行pthread_mutex_unlock之后A线程才得以继续执行。
如果pthread_cond_signal前后没有使用互斥锁count_lock保护,可能的情况是这样。A阻塞到 pthread_cond_wait,然后B执行到pthread_cond_signal时候,发生了线程切换,于是A被唤醒,并且发现count依然 是0,所以继续阻塞到条件锁count_nonzero上。然后B继续执行,这时候尽管count=1,A永远不会被唤醒了。这样就发生了逻辑错误。
ps:
要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0 等等,这一点我们从后面的
例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while 语句实现。
当然在这个上下文中,如果把count=count+1放在函数放在pthread_cond_signal之前变成.因此上述A唤醒后仍会检查count是否为0
increment_count(){
count=count+1;
if(count==0)
pthread_cond_signal(&count_nonzero);
}
这样没有问题。但是这种方法并不能保证所有情况下都适用。于是需要用互斥锁保护条件锁相关的变量。也就是说条件锁是用来线程通讯的,但是互斥锁是为了保护这种通讯不会产生逻辑错误,可以正常工作。
一个Condition Variable总是和一个Mutex搭配使用的。一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:
-
释放Mutex
-
阻塞等待
-
当被唤醒时,重新获得Mutex并返回
pthread_cond_timedwait函数还有一个额外的参数可以设定等待超时,如果到达了abstime所指定的时刻仍然没有别的线程来唤醒当前线程,就返回ETIMEDOUT。一个线程可以调用pthread_cond_signal唤醒在某个Condition Variable上等待的另一个线程,也可以调用pthread_cond_broadcast唤醒在这个Condition Variable上等待的所有线程