并行线程:调用pthread_cond_signal()从临界部分内(pthreads : pthr

2019-09-02 02:26发布

我在线程A的下面的代码段,其中使用块pthread_cond_wait()

pthread_mutex_lock(&my_lock);     
if ( false == testCondition )        
    pthread_cond_wait(&my_wait,&my_lock); 
pthread_mutex_unlock(&my_lock);

我在线程B下面的代码段,该信号线程A

pthread_mutex_lock(&my_lock);  
testCondition = true;
pthread_cond_signal(&my_wait);
pthread_mutex_unlock(&my_lock);

提供没有其他线程,这将使任何区别,如果pthread_cond_signal(&my_wait)被移出临界段块的,如下所示?

pthread_mutex_lock(&my_lock);  
testCondition = true;
pthread_mutex_unlock(&my_lock);
pthread_cond_signal(&my_wait);

Answer 1:

我的建议是一般保持pthread_cond_signal()锁定区域内的电话,但可能不是你认为的原因。

在大多数情况下,它并没有真正不管你调用pthread_cond_signal()在持有或不锁。 本是正确的,有些调度可以强制上下文切换的时候,如果有另一个线程等待释放锁,让你的线程可能会被切离,才可以调用pthread_cond_signal() 在另一方面,一些调度会尽快调用运行等待的线程pthread_cond_signal()所以如果你在持有锁的调用它,等待的线程将被唤醒,然后向右走回去睡觉(因为它现在已经封锁互斥),直到信号线进行解锁。 确切的行为是高度实现特定的操作系统版本之间可能会发生变化,所以它是没有什么可以依靠。

但是,这一切看起来过去什么应该是你的首要关注的问题,这是你的代码的可读性和正确性。 你可能不会看到由这种微优化的任何真实世界的性能优势(记住优化的第一条规则:简介第一,优化秒)。 然而,它更容易想到的控制流程,如果你知道这组等待线程不能在您设定的条件并发送信号点之间切换。 否则,你必须要考虑这样的事情,“如果有什么线程A组testCondition=TRUE和释放锁,然后线程B运行并看到testCondition是真实的,所以它跳过pthread_cond_wait()并继续重置testConditionFALSE ,然后最后一个线程运行,并调用pthread_cond_signal()它唤醒线程C,因为线程B实际上并没有等待,而是testCondition是不是真的了。” 这是混乱的,并可能导致在你的代码难以诊断的竞争条件。 出于这个原因,我认为这是更好地与持有的锁信号; 这样一来,你知道设置条件和发送信号是原子相对于对方。

与此相关的,你现在的样子调用pthread_cond_wait()不正确。 这是可能的(虽然很少)的pthread_cond_wait()没有条件变量实际上被通知给退货,并有其他情况下(例如,在比赛中我如上所述),其中的信号可能最终唤醒一个线程,即使条件ISN” Ť如此。 为了安全起见,你需要把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);

(我也修正了可能在你原来的例子,它是利用一个错字my_mutexpthread_cond_wait()调用,而不是my_lock 。)



Answer 2:

等待条件变量的线程应该保持锁定互斥,其他线程应始终锁定互斥信号。 这样一来,你知道其他线程在等待状态时,你发送的信号。 否则,有可能等待的线程将不会看到被发送信号的条件,将阻止无限期地等待就可以了。

条件变量通常用于这样的:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int go = 0;

void *threadproc(void *data) {
    printf("Sending go signal\n");
    pthread_mutex_lock(&lock);
    go = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);
}

int main(int argc, char *argv[]) {
    pthread_t thread;
    pthread_mutex_lock(&lock);
    printf("Waiting for signal to go\n");
    pthread_create(&thread, NULL, &threadproc, NULL);
    while(!go) {
        pthread_cond_wait(&cond, &lock);
    }
    printf("We're allowed to go now!\n");
    pthread_mutex_unlock(&lock);
    pthread_join(thread, NULL);
    return 0;
}

这是有效的

void *threadproc(void *data) {
    printf("Sending go signal\n");
    go = 1;
    pthread_cond_signal(&cond);
}

然而,考虑发生了什么事在main

while(!go) {
    /* Suppose a long delay happens here, during which the signal is sent */
    pthread_cond_wait(&cond, &lock);
}

如果该评论所描述的延迟现象, pthread_cond_wait会留下等待,可能是永远。 这就是为什么你想锁定的互斥信号。



Answer 3:

两者都是正确的,但是,对于反应的问题,最调度到另一个线程给手锁被释放时。 我不解锁信号之前,你的等待线程A是不是在准备名单和古都不会安排到B被再次安排并调用pthread_cond_signal会()。



Answer 4:

在公开组基本规范问题7 IEEE标准1003.1,2013版 (其中据我可以告诉是官方的并行线程规范)说,这对此事:

的调用pthread_cond_broadcast()或调用pthread_cond_signal()的功能可以由一个线程它是否当前拥有那个线程调用调用pthread_cond_wait()或那么pthread_cond_timedwait()都与在其等待状态变量相关联的互斥被调用; 然而,如果需要可预测的调度行为,则该互斥应由线程调用调用pthread_cond_broadcast()或调用pthread_cond_signal()锁定。

要增加我个人的经验,我正在上有代码,其中条件变量被破坏的应用程序(和存储含有它释放)由被唤醒线程。 我们发现,多核设备(iPad的空气2)调用pthread_cond_signal()在实际上可能有时会死机,如果它是互斥锁外,作为服务员醒来,破坏了条件变量调用pthread_cond_signal已完成之前。 这是相当意外。

所以我肯定会朝版“内锁信号”改变方向,这似乎是更安全的。



Answer 5:

这是很好的写了有关条件变量: 为提高应用程序使用POSIX线程条件变量的可扩展性技巧 (下看“避免互斥争”部分和7点)

它说,第二个版本可能有一定的性能优势。 因为它能使线程与调用pthread_cond_wait等待那么频繁。



文章来源: pthreads : pthread_cond_signal() from within critical section