在线程中等待标志的最佳方法

Posted

技术标签:

【中文标题】在线程中等待标志的最佳方法【英文标题】:Best way to wait for flag in thread 【发布时间】:2019-07-30 22:31:35 【问题描述】:

我有一个运行多个线程的 C++ 程序。我正在使用全局标志来告诉我什么时候可以继续我的程序。这需要使用等待设置标志的 while 循环。我知道这种方法不是最好的,因为程序运行的时间比预期的要长得多。

我已经看到了互斥体和条件变量的使用,但我不确定它是否适用于我的需求或如何实现。

volatile bool flag;

int threadFunction()
    //Do some initialization work
    //
    //Then look for flag to be set
    while (1)
        if (flag)
            //do stuff
            flag = false;
        
        Sleep(1);
    


int setFlag()
    flag = true;


int main()
    std::thread thread1(threadFunction);
    while (1)
        setFlag();
        Sleep(5);
    
    thread1.join()

threadFunction 中的 while 循环似乎导致了速度问题。有没有更好的方法来做到这一点?

没有错误,但代码似乎效率很低。

【问题讨论】:

与您的问题没有任何关系,但几乎总是if (flag) do_stuff(); flag = false; 是错误的。更好的是if (flag) flag = false; do_stuff(); 【参考方案1】:

您目前拥有的是忙碌的等待,有时也称为自旋锁。这些可以在某些情况下提供良好的性能(主要是在等待时间很短且不频繁的情况下),但也可能导致 CPU 负载过度占用 CPU 内核,而这些 CPU 内核可能正在执行生产性工作。

另一个选项是阻塞标志。这将使用操作系统提供的工具在等待某些条件时阻止您的线程。这将防止它无所事事地占用 CPU 资源,因为操作系统知道只要您的线程正在等待,它就可以调度另一个线程。它还可能增加延迟,因为一旦您的线程等待的条件发生,操作系统必须重新安排您的线程。使用自旋锁,您的线程已经在执行,并且可以使用其剩余时间片立即进行生产性工作。

您可以从条件变量和互斥体构建一个简单的阻塞标志:

class Flag

public:
    Flag() : flag_false 

    void set()
    
        std::lock_guard g(mutex_);
        flag_ = true;
        cond_var_.notify_all();
    

    void clear()
    
        std::lock_guard g(mutex_);
        flag_ = false;
    

    void wait()
    
        std::unique_lock lock(mutex_);
        cond_var_.wait(lock, [this]()  return flag_; );
    
private:
    bool flag_;
    std::mutex mutex_;
    std::condition_variable cond_var_;
;

Live Demo


您需要哪种类型的锁定机制在很大程度上取决于您的特定工作负载。与所有性能问题一样,了解哪个最适合您的唯一方法是进行基准测试。

【讨论】:

flag 应该是std::atomic,我想。 @PaulSanders 它不需要是原子的。它是谓词数据,因此 all 访问应该已经是互斥的,这要归功于该互斥锁。 @PaulSanders flag_ 在这里不需要是原子的,因为互斥体提供了内存屏障。 @MilesBudnek 这没什么大不了的,但是将 notify_all 调用移到包含互斥锁的块内可以提高性能。然后,一个好的实现知道notify_all 调用不可能使线程准备好运行并且不能与任何东西抗衡,从而允许它在快速路径上执行。由于 OP 关心性能,因此最好的方式似乎是一个好主意。 @DavidSchwartz 有趣。我总是听到相反的建议:当你持有相关的互斥锁时,你应该避免通知等待条件变量的线程,因为线程会唤醒并立即再次阻塞。我将不得不做一些进一步的阅读。【参考方案2】:

目前您在代码中有“忙等待”和/或延迟问题。

使用std::condition_variablestd::mutex。 有关详细信息,请参阅 C++ 参考。它包含您需要的所有示例和说明。

【讨论】:

同样volatile bool flag;应该是std::atomic <bool> flag;

以上是关于在线程中等待标志的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

对于等待工作线程的主机线程,我应该使用哪种内存顺序?

java 线程 wait join sleep yield notify notifyall synchronized

线程的终止

如何实现线程互等,线程2等待线程1结束后才继续执行。(可设置标志位) 求源代码

等待线程退出条件的正确方法

事件对象