我读到某个地方,我们应该在调用pthread_cond_signal之前锁定互斥锁,并在调用它之后解锁mutext:
pthread_cond_signal()例程用于发信号(或唤醒)另一个正在等待条件变量的线程。它应该在互斥锁锁定后调用,并且必须解锁互斥锁才能完成pthread_cond_wait()例程。
我的问题是:在不锁定互斥锁的情况下调用pthread_cond_signal或pthread_cond_broadcast方法是否可以?
Answers:
如果未将互斥锁锁定在更改条件和信号的代码路径中,则可能会丢失唤醒。考虑这对过程:
流程A:
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);
现在condition
是TRUE
,但是进程A滞留在条件变量上等待-它错过了唤醒信号。如果我们更改进程B来锁定互斥锁:
进程B(正确):
pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
...那么上述情况就不会发生;唤醒将永远不会被错过。
(请注意,您实际上可以将pthread_cond_signal()
自身移动到之后pthread_mutex_unlock()
,但是这可能导致线程的最佳调度不太理想,并且由于更改了条件本身,您已经锁定了该代码路径中的互斥锁)。
根据本手册:
该
pthread_cond_broadcast()
或pthread_cond_signal()
功能 可以通过它是否当前拥有互斥线程调用的线程调用pthread_cond_wait()
或pthread_cond_timedwait()
已与在他们等待的条件变量相关联; 但是,如果需要可预测的调度行为,则该互斥锁应由线程调用pthread_cond_broadcast()
或 锁定pthread_cond_signal()
。
可预测调度行为声明的含义由comp.programming.threads上的Dave Butenhof(使用POSIX线程编程的作者)解释了,并在此处提供。
caf,在示例代码中,流程B进行了修改,condition
而没有先锁定互斥锁。如果进程B只是在修改期间锁定了互斥锁,然后在调用之前仍然解锁了互斥锁pthread_cond_signal
,那将没有问题---我是否正确?
我直觉上相信caf的位置是正确的:pthread_cond_signal
不拥有互斥锁就打电话是个坏主意。但是,caf的榜样实际上并不是支持这一立场的证据。这只是证明弱点(实际上不言而喻)的证据,除非您先锁定了该互斥锁,否则修改受互斥锁保护的共享状态是一个坏主意。
谁能提供一些示例代码,其中调用pthread_cond_signal
后pthread_mutex_unlock
产生正确的行为,但调用pthread_mutex_unlock
后pthread_cond_signal
产生错误的行为?
pthread_cond_signal
后,pthread_mutex_unlock
可能会导致丢失唤醒,因为信号被捕获是“错误”的线程,一个看到的谓词在更改之后受阻。仅当同一条件变量可用于多个谓词并且您不使用时pthread_cond_broadcast
,这才是问题,无论如何这是一种罕见且脆弱的模式。
pthread_signal_cond()
可以在互斥锁解锁后移动“正确”路径,尽管最好不要这样做。也许更正确地说,在您要调用的位置pthread_signal_cond()
,您已经需要锁定互斥锁才能修改条件本身。