【问题标题】: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(); }

标签: c++ windows multithreading


【解决方案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;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-04
  • 1970-01-01
  • 2015-10-18
相关资源
最近更新 更多