在不锁定互斥锁的情况下调用pthread_cond_signal

Mey*_*sam 78 c++ mutex signals pthreads condition-variable

我在某处读到我们应该在调用pthread_cond_signal之前锁定互斥锁并在调用它后解锁互斥锁:

pthread_cond_signal()例程用于发信号(或唤醒)正在等待条件变量的另一个线程.它应该在锁定互斥锁后调用,并且必须解锁互斥锁才能完成pthread_cond_wait()例程.

我的问题是:在不锁定互斥锁的情况下调用pthread_cond_signalpthread_cond_broadcast方法是不是可以?

caf*_*caf 136

如果未在代码路径中锁定互斥锁以更改条件和信号,则可能会丢失唤醒.考虑这对过程:

流程A:

pthread_mutex_lock(&mutex);
while (condition == FALSE)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
Run Code Online (Sandbox Code Playgroud)

流程B(不正确):

condition = TRUE;
pthread_cond_signal(&cond);
Run Code Online (Sandbox Code Playgroud)

然后考虑这种可能的指令交错,其condition开头为FALSE:

Process A                             Process B

pthread_mutex_lock(&mutex);
while (condition == FALSE)

                                      condition = TRUE;
                                      pthread_cond_signal(&cond);

pthread_cond_wait(&cond, &mutex);
Run Code Online (Sandbox Code Playgroud)

现在conditionTRUE,但是进程A等待条件变量 - 它错过了唤醒信号.如果我们改变进程B来锁定互斥锁:

流程B(正确):

pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
Run Code Online (Sandbox Code Playgroud)

......那么上面就不会发生; 唤醒永远不会错过.

(请注意,您实际上可以pthread_cond_signal()自身之后移动自身pthread_mutex_unlock(),但这可能会导致线程的优化调度不佳,并且由于更改条件本身,您必须已锁定此代码路径中的互斥锁).

  • 对于它的价值,似乎在信号之前或之后是否解锁实际上是非常重要的问题.解锁后确保优先级较低的线程无法从较高优先级的线程中窃取事件,但如果您没有使用优先级,则在信号实际减少系统调用/上下文切换次数之前解锁提高整体表现. (7认同)
  • @Nemo:是的,在"正确"的路径中,`pthread_signal_cond()`*可以在互斥锁解锁后移动,尽管最好不要这样做.或许更正确地说,在你调用`pthread_signal_cond()`时,你已经需要锁定互斥锁来修改条件本身. (3认同)
  • @R .. 你倒退了。在信号之前解锁会增加系统调用的数量并降低整体性能。如果您在解锁之前发出信号,则实现知道该信号不可能唤醒线程(因为在 cv 上阻塞的任何线程都需要互斥锁才能向前推进),从而允许它使用快速路径。如果在解锁后发出信号,解锁和信号都可以唤醒线程,这意味着它们都是昂贵的操作。 (2认同)
  • @Alceste_:请参阅 [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html) 中的此文本:*“当线程等待条件变量时,已将特定的互斥体指定给任一`pthread_cond_timedwait()` 或 `pthread_cond_wait()` 操作,在互斥锁和条件变量之间形成动态绑定,只要至少有一个线程在条件变量上被阻塞,该绑定就会保持有效。在此期间,效果任何线程尝试使用不同的互斥锁等待该条件变量的尝试是未定义的。*” (2认同)

ice*_*ime 47

根据本手册:

pthread_cond_broadcast()pthread_cond_signal()功能 可以由一个线程是当前是否拥有互斥体被称为该线程调用pthread_cond_wait()pthread_cond_timedwait()已与在他们等待的条件变量相关联; 但是,如果需要可预测的调度行为,那么该互斥锁应由线程调用pthread_cond_broadcast()或 锁定 pthread_cond_signal().

Dave Butenhof(编程与POSIX线程的作者)在comp.programming.threads上解释了可预测调度行为语句的含义,可在此处获得.

  • +1用于链接Dave Butenhof的邮件.我自己总是想知道这个问题,现在我知道了......今天学到了一些重要的东西.谢谢. (6认同)

Quu*_*one 5

caf,在示例代码中,流程B进行了修改,condition而没有先锁定互斥锁。如果进程B在修改过程中只是简单地锁定了互斥锁,然后在调用之前仍然解锁了互斥锁pthread_cond_signal,那将没有问题---我是否正确?

我直觉上相信caf的位置是正确的:pthread_cond_signal不拥有互斥锁就打电话是个坏主意。但是,caf的榜样实际上并不是支持这一立场的证据。仅是证明弱点(实际上是不言而喻)的证据,除非您先锁定了该互斥锁,否则修改由互斥锁保护的共享状态是一个坏主意。

谁能提供一些示例代码,其中调用pthread_cond_signalpthread_mutex_unlock产生正确的行为,但调用pthread_mutex_unlockpthread_cond_signal产生错误的行为?

  • 实际上,我认为我的问题是[this](http://stackoverflow.com/questions/6419117/signal-and-unlock-order)的重复,答案是“很好,您可以*完全*致电pthread_cond_signal不用拥有互斥锁。没有正确性问题。但是在常见的实现中,您会错过对pthread深入的聪明的优化,因此在保持锁的情况下调用pthread_cond_signal稍微可取。 (2认同)