pthreads:来自临界区内的pthread_cond_signal()

cur*_*age 14 mutex signals pthreads critical-section

我在线程A中有以下代码,它阻止使用 pthread_cond_wait()

pthread_mutex_lock(&my_lock);     
if ( false == testCondition )        
    pthread_cond_wait(&my_wait,&my_lock); 
pthread_mutex_unlock(&my_lock);
Run Code Online (Sandbox Code Playgroud)

我在线程B中有以下代码,它代表线程A

pthread_mutex_lock(&my_lock);  
testCondition = true;
pthread_cond_signal(&my_wait);
pthread_mutex_unlock(&my_lock);
Run Code Online (Sandbox Code Playgroud)

如果没有其他线程,如果pthread_cond_signal(&my_wait)移出临界区块,它会有什么不同,如下所示?

pthread_mutex_lock(&my_lock);  
testCondition = true;
pthread_mutex_unlock(&my_lock);
pthread_cond_signal(&my_wait);
Run Code Online (Sandbox Code Playgroud)

小智 17

我的建议通常是将pthread_cond_signal()呼叫保持在锁定区域内,但可能不是您认为的原因.

在大多数情况下,无论您是否pthread_cond_signal()持有锁,都无关紧要.Ben是正确的,如果有另一个线程等待,一些调度程序可能会在释放锁时强制进行上下文切换,因此您的线程可能会在它可以调用之前被切换掉pthread_cond_signal().另一方面,一些调度程序将在您调用时立即运行等待线程pthread_cond_signal(),因此如果您在锁定时调用它,则等待线程将被唤醒然后再回到睡眠状态(因为它现在在互斥锁上被阻止)直到信令线程解锁它.确切的行为是高度特定于实现的,并且可能在操作系统版本之间发生变化,因此它不是您可以依赖的任何内容.

但是,所有这些都看起来应该是您最关心的问题,即代码的可读性和正确性.您不太可能从这种微优化中看到任何实际的性能优势(请记住优化的第一条规则:首先进行配置,然后优化第二条).但是,如果您知道等待线程集在您设置条件的点和发送信号之间无法更改,则更容易考虑控制流.否则,你必须要考虑的事情,如"什么,如果线程A组testCondition=TRUE和释放锁,然后线程B运行,并认为这testCondition是真实的,所以它会跳过pthread_cond_wait()并继续重置testConditionFALSE,最后线程A运行,并调用pthread_cond_signal(),它唤醒了线程C,因为线程B实际上并没有等待,但testCondition不再是真的".这很令人困惑,并且可能导致代码中难以诊断的竞争条件.出于这个原因,我认为最好用锁定信号发出信号; 这样,你知道设置条件和发送信号是相互原子的.

在相关的说明中,您的呼叫pthread_cond_wait()方式不正确.在pthread_cond_wait()没有条件变量实际发出信号的情况下返回是可能的(尽管很少见),并且还有其他情况(例如,我上面描述的竞赛),即使条件不是真的,信号也可能最终唤醒线程.为了安全起见,您需要将pthread_cond_wait()调用置于while()测试条件的循环中,以便pthread_cond_wait()在重新获取锁定后如果不满足条件则回调.在您的示例中,它看起来像这样:

pthread_mutex_lock(&my_lock);     
while ( false == testCondition ) {
    pthread_cond_wait(&my_wait,&my_lock);
}
pthread_mutex_unlock(&my_lock);
Run Code Online (Sandbox Code Playgroud)

(我还纠正了原始示例中可能是拼写错误的内容,这是my_mutex用于pthread_cond_wait()调用而不是my_lock.)


Ben*_*Ben 1

两者都是正确的,但是对于反应性问题,大多数调度程序在释放锁时将手交给另一个线程。如果您在解锁之前没有发出信号,则您的等待线程 A 不在就绪列表中,并且在再次调度 B 并调用 pthread_cond_signal() 之前,不会调度该线程。