【问题标题】:condition variable - why calling pthread_cond_signal() before calling pthread_cond_wait() is a logical error?条件变量 - 为什么在调用 pthread_cond_wait() 之前调用 pthread_cond_signal() 是一个逻辑错误?
【发布时间】:2011-07-29 01:11:19
【问题描述】:

它是用 POSIX 线程教程编写的 https://computing.llnl.gov/tutorials/pthreads/ 这是一个逻辑错误。

我的问题是为什么这是一个逻辑错误?

在我的程序中我需要使用这些信号,但是我不能保证会有一个线程处于 _cond_wait 状态。我试图测试它,但没有任何反应。这会导致意外行为或更糟吗?

谢谢!

【问题讨论】:

    标签: c++ c pthreads condition-variable


    【解决方案1】:

    条件变量允许一个线程从等待中唤醒另一个线程。它们仅在您触发条件时有线程等待时才起作用。确保这种情况的方法是等待线程锁定与条件链接的互斥体,并让信号线程在触发条件之前锁定该互斥体。换句话说,只有当另一个线程已经锁定了互斥体但现在正在等待时,信号线程才能锁定互斥体并触发条件。

    我最熟悉 boost,所以我将在这个例子中使用它:

    // A shared mutex, global in this case.
    boost::mutex myMutex;
    
    // Condition variable
    boost::condition_variable myCondition;
    
    void threadProc()
    {
        // Lock the mutex while the thread is running.
        boost::mutex::scoped_lock guard( myMutex );
    
        while( true )
        {
            // Do stuff, then...
    
            myCondition.wait( guard ); // Unlocks the mutex and waits for a notification.
        }
    }
    
    void func()
    {
        // Function wants to trigger the other thread. Locks the mutex...
        boost::mutex::scoped_lock guard( myMutex );
    
        // Since the mutex is locked, we know that the other thread is
        // waiting on the condition variable...
        myCondition.notify_all();
    }
    

    在没有相应等待的情况下向条件变量发出信号是一个逻辑错误,因为什么都不会收到信号。条件变量不会保持信号状态。

    【讨论】:

      【解决方案2】:

      我的 2 美分:当没有线程被阻塞调用 *pthread_cond_wait()* 时,我不知道调用 *pthread_cond_signal()* 的副作用。这确实是一个实现细节 我认为,如果您的线程/定时模型不能保证等待和信号之间的正确顺序,那么您可能应该考虑使用不同的同步机制 [例如简单的 信号量]即使线程 A 尚未到达同步点,也会从线程 B 发出信号量信号。当线程 A 到达同步点时,它会发现信号量增加并进入关键会话。

      【讨论】:

      • +1 用于建议信号量,通常更容易正确使用。
      • 我真的很关心时序模型。似乎程序员必须知道自主线程中动作的顺序不是“正确的抽象级别”。我不喜欢它,我一直在避免使用信号和等待命令,因为它似乎迟早会导致错误。
      【解决方案3】:

      如果您不在乎此信号会丢失 - 没有错误。如果您期望稍后到来的等待线程立即从 cond_wait() 唤醒,这只是一个错误。

      由于这是 pthread_cond 的常见用例,因此教程称之为逻辑错误。但是什么都不会崩溃,也不会发生意外的行为。在正常的执行流程中,当 cond_wait() 中没有线程时,仍然可能会发出 cond_signal():f.e.,当 writer 在队列中添加另一个数据片段时,所有 reader 可能只是在进行消息处理。

      【讨论】:

        【解决方案4】:

        blaze 的答案最接近,但并不完全清楚:
        条件变量只能用于表示条件发生变化

        线程 1 检查条件。如果条件不满足,他会等待条件变量,直到条件满足。因为先检查条件,所以他不应该关心条件变量是否被发出信号:

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

        线程 2 更改条件并通过条件变量发出更改信号。他不在乎线程是否在等待:

        pthread_mutex_lock(&mutex); 
        changeCondition(); 
        pthread_mutex_unlock(&mutex); 
        pthread_cond_signal(&cond)
        

        底线是:通信是通过某种条件完成的。条件变量只会唤醒等待的线程,以便它们检查条件

        条件示例:

        • 队列不为空,因此可以从队列中取出一个条目
        • 设置了一个布尔标志,因此线程等待 s 直到另一个线程发出可以继续的信号
        • 位集中的一些位被设置,所以等待线程可以处理相应的事件

        另见pthread example

        【讨论】:

        • 在第二个代码中,我认为 pthread_cond_signal(&cond) 应该在互斥锁之间
        • @Duke:不,不应该。我以前也这么认为,但没有理由这样做,当接收方具有相同或更高的优先级时,您有 2 个额外的上下文切换,因为接收方必须阻塞,直到发送方解锁互斥锁。
        • @Konstantin 谢谢,请注意,更现代的构造(例如c++11 threading)可以帮助您以这种方式使用条件变量。
        • @LuisAverhoff:线程 1 几乎总是在互斥锁解锁的 pthread_cond_wait() 调用中。这就是为什么必须将互斥锁传递给此调用的原因。所以线程 2 只需要在线程 1 实际检查条件时等待。这就是互斥锁的用途:保护构成条件的资源..
        • 谢谢,这解释了我在我的应用程序中调试的行为:我的startThread() 函数在线程启动时立即退出,但尚未进入pthread_cond_wait。如果我立即调用stopThread() 并在其中设置predicate=true; pthread_cond_signal(),那么我的线程没有唤醒,因为信号来得太早了。现在我知道这是设计使然,当我不确定线程​​是否在等待它时,我不应该设置 cond_signal。解决方法很简单 - 在输入 pthread_cond_wait 之前,在线程循环中检查一次谓词。
        【解决方案5】:

        我写下我的答案是因为我没有看到能让人们平静下来的答案。我还偶然发现了那个教程中关于“逻辑错误”的奇怪而令人不安的警告。请注意,the POSIX documentation article on pthread_cond_signal 中没有关于此“错误”的内容。我确信这是对术语的不幸选择,或者是本教程作者的一个明显错误。他们的主张可能会被解释为进程将在这种情况下因错误而终止,或者任何允许这种情况的程序都是不正确的。没有什么是真的。这种情况很常见。文档说

        pthread_cond_signal()pthread_cond_broadcast() 函数具有 如果cond 上当前没有线程阻塞,则无效。

        所以不要担心,要快乐。

        【讨论】:

        • 我认为这是迄今为止最好的答案。首先,它确实回答了“为什么......错误”的问题,而被接受和投票最多的问题却没有。二是简单易懂。
        • @RenaudPacalet 很晚才回复您的评论。我的回答确实没有直接回答这个问题,但它指的是已经回答了这个问题的stackoverflow.com/a/5537231/104774(由 blaze 回答)。我认为我的“澄清”当时有一些赞成票,因为应该如何使用 cv 并不是那么明显。有时它被用作简单的屏障,接收器只是在等待信号继续,结果是令人困惑的错误。顺便说一句,本教程似乎暗示在调用 pthread_cond_signal 时必须锁定互斥锁,这也是不正确的。
        猜你喜欢
        • 2018-03-27
        • 2022-09-29
        • 1970-01-01
        • 1970-01-01
        • 2012-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多