89

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

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

我的问题是:不锁定互斥体就可以调用pthread_cond_signalpthread_cond_broadcast方法吗?

4

3 回答 3

158

如果您没有在更改条件和信号的代码路径中锁定互斥锁,您可能会丢失唤醒。考虑这对过程:

过程一:

pthread_mutex_lock(&mutex);
while (condition == FALSE)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

过程 B(不正确):

condition = TRUE;
pthread_cond_signal(&cond);

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

Process A                             Process B

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

                                      condition = TRUE;
                                      pthread_cond_signal(&cond);

pthread_cond_wait(&cond, &mutex);

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

过程 B(正确):

pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

...那么上述情况就不会发生;唤醒永远不会错过。

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

于 2010-12-31T03:38:23.350 回答
54

根据本手册:

pthread_cond_broadcast()or pthread_cond_signal()函数 可以被线程调用,无论它当前是否拥有线程调用的互斥体或在pthread_cond_wait() 等待pthread_cond_timedwait()期间与条件变量相关联;但是,如果需要可预测的调度行为,则该互斥锁应由调用 pthread_cond_broadcast()或 的线程锁定pthread_cond_signal()

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

于 2010-12-28T07:48:07.770 回答
8

caf,在您的示例代码中,进程 B 进行修改condition而不首先锁定互斥锁。如果进程 B 只是在修改期间锁定了互斥锁,然后在调用之前仍然解锁了互斥锁pthread_cond_signal,那么就没有问题 --- 我是对的吗?

我凭直觉相信 caf 的立场是正确的:pthread_cond_signal在不拥有互斥锁的情况下调用是一个坏主意。但是 caf 的例子实际上并不是支持这一立场的证据;它只是支持弱得多(实际上不言而喻)立场的证据,即除非您首先锁定该互斥锁,否则修改受互斥锁保护的共享状态是一个坏主意。

任何人都可以提供一些示例代码,其中调用pthread_cond_signal之后会pthread_mutex_unlock产生正确的行为,但调用pthread_mutex_unlock之后会pthread_cond_signal产生不正确的行为?

于 2012-06-14T23:48:25.857 回答