条件变量singal与unlock的顺序

背景

在看hotsport源码的时候遇到一段signal与unlock顺序不一样的代码,在Parker::unpark里面:

if (WorkAroundNPTLTimedWaitHang) {
  status = pthread_cond_signal (&_cond[_cur_index]);
  assert (status == 0, "invariant");
  status = pthread_mutex_unlock(_mutex);
  assert (status == 0, "invariant");
} else {
  status = pthread_mutex_unlock(_mutex);
  assert (status == 0, "invariant");
  status = pthread_cond_signal (&_cond[_cur_index]);
  assert (status == 0, "invariant");
}

默认走的是上面那段。

函数原型

pthread_cond_wait

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)

该函数做了两件事情:首先释放第二个参数指向的锁,然后把第一个参数cond表示的条件变量指向条件阻塞变量,直到条件信号被激活。

当一个线程在使用条件变量和互斥锁的时候,在条件不满足的时候(即条件信号为阻塞的时候),通过这个函数,该线程的锁会不断被解锁最后又被加锁,直到条件满足,被激活后此时会到重新上锁那一步,最后返回该函数,此时该函数执行语句结束,才会往下面的程序进行。

注意

wait之前先lock

调用thread_cond_wait()之前,必须先lock相关联的mutex, 因为假如目标条件未满足,pthread_cond_wait()实际上会unlock该mutex, 然后block,在目标条件满足后再重新lock该mutex, 然后返回。

其他线程通过pthread_cond_signal()或pthread_cond_broadcast把当前线程唤醒后,
pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。

必须被包装在while里面判断

因为在signal和wait之间会有时差,这个时差中可能有其他线程消耗了可被满足的条件。

pthread_mutex_lock(mutex);
while (!predicate)
  pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);

建议在signal之后unlock

singal和unlock顺序可以任意,但是建议在singal之后unlock,否则可能出现虚假唤醒。

当锁被释放时,如果有另一个线程在等待,一些调度器可能会强制进行上下文切换,所以你的线程可能会在调用pthread_cond_signal()之前被切换掉。

https://stackoverflow.com/questions/6419117/signal-and-unlock-order 这里聊了一种情况:

  1. 线程A阻塞等待一个队列中的数据
  2. 线程B获取到mutex,插入数据,unlock,然后没来得及signal就被切换走了
  3. 线程C获取到mutex,插入数据,unlock,signal
  4. 线程A获取到mutex,wait返回,处理了B和C插入的数据,之后继续阻塞
  5. 线程B获取到时间片,完成2未完成的任务:signal
  6. 线程A被虚假唤醒,队列为空,继续阻塞

不过作者也说了,问题不大,顺序不需要太关心,除非使用者知道系统是否有实现signal后不唤醒只是让线程去排队。

refer:
https://stackoverflow.com/questions/1640389/pthreads-pthread-cond-signal-from-within-critical-section
https://blog.csdn.net/qq_36573828/article/details/76708162

comments powered by Disqus