如何在 Linux 上同时等待两个条件?

Posted

技术标签:

【中文标题】如何在 Linux 上同时等待两个条件?【英文标题】:How do I wait for two conditions simultaneously on Linux? 【发布时间】:2012-08-04 02:33:43 【问题描述】:

考虑一个系统,其中一堆线程等待某些事务完成(请注意,多个线程可以等待单个事务)。为了使事务完成,其中一个线程必须运行一个调度循环。一旦当前运行调度循环的线程正在等待的事务完成,其他线程之一必须接管该工作。

在 Windows 上,这很容易实现:对于每个事务,事务完成时都会设置一个手动重置事件。此外,当调度循环退出时,会设置一个自动重置事件。每个线程同时等待两个事件。事务事件首先发出信号——在这种情况下线程退出——或者循环事件——在这种情况下线程运行调度循环。

如何在 Linux(或者更好的 Posix)上实现这一点?目前,我已经用bool 变量替换了上面的事件,并且我有一个条件变量来表示其中一个发生了变化。但是,在这种安排中,线程会虚假地唤醒(每当一个事务完成时,所有线程都会唤醒)。有没有办法更好地实现这一点?

【问题讨论】:

只是检查一下:您已经将pthread_cond_wait() 包装在 mutex 区域中,对吧?至于处理多个事件,semaphore 在唤醒线程之前计算您期望的预期前置条件的数量呢? @chrisaycock,是的,当然,其中涉及互斥锁。至于信号量的想法,请注意,我需要线程在 either 事件发生时唤醒,不一定同时发生。还是我误解了你的建议? 您可以通过让线程在第二个地址上休眠而不是让线程在第二个地址上休眠来假装在事务上休眠,而是让完成事务的进程向等待它的线程发出信号,该线程捕获信号并退出 【参考方案1】:

我可能没有完全清楚您的情况,但我认为您可以在每个事务中使用一个条件变量(但仍然是一个互斥体)。

当事务完成时,其对应的条件变量会发出信号;当调度循环退出时,所有的条件变量都会被发出信号。

【讨论】:

谢谢,这是个好主意!但是,当调度循环退出时,仍然会有虚假的唤醒(在 Windows 上,当自动重置事件发出信号时,只有一个线程被唤醒)。有没有解决的办法?无论如何 +1。 @avakar: 如果 any 的其他线程可以接管调度循环,您可以选择一个尚未退出的随机事务并向该条件变量发出信号,而不是全部发信号。【参考方案2】:

我可能误解了您的要求。我会为一个事件创建一个抽象,并有一个事件队列。派生事件将被排队,并且线程正在等待队列非空的条件。入队会在条件变量上产生信号。

struct AnEvent 
    int type_;
    union 
        //...
    ;
;

struct EventQ 
    std::mutex m_;
    std::condition_variable c_;
    std::list<AnEvent> q_;

    void enq (const AnEvent &e) 
        std::lock_guard<std::mutex> g(m_);
        bool was_empty = q_.empty();
        q_.push_back(e);
        if (was_empty) c_.notify_one();
    ;

    void deq (AnEvent &e) 
        std::lock_guard<std::mutex> g(m_);
        while (q_.empty()) c_.wait(m_);
        e = q_.top();
        q_.pop_front();
        if (!q_.empty()) c_.notify_one();
    

【讨论】:

谢谢,问题是在你的场景中,一个随机线程会在事务完成后被唤醒。但是,多个线程可能正在等待同一个事务,此外,随机唤醒的线程可能正在等待不同的事务。 我想我对您的问题的了解不完整,对如何使用该解决方案的解释也不完整。如果每个线程都有一个EventQ 等待,那么它会在队列中等待事务事件或调度事件。您的实现将强制只存在调度事件的单个实例。

以上是关于如何在 Linux 上同时等待两个条件?的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 上等待多个条件变量而没有不必要的睡眠?

如何同时启动两个功能,只等待更快的一个?

实战并发编程 - 07循环等待&死锁问题

linux网络编程-posix条件变量(40)

主程序如何通过等待而不是加入同时等待多个线程?

如何在 R 中迭代地过滤列表中的列表或如何同时使用两个条件过滤 data.table,在运行时创建对象