如何在睡眠时唤醒std :: thread

Gam*_*ads 14 c++ mutex condition-variable c++11 stdthread

我正在使用C++ 11,我有一个std::thread类成员,它每2分钟向听众发送一次信息.其他它只是睡觉.所以,我让它睡了2分钟,然后发送所需的信息,然后再睡2分钟.

// MyClass.hpp
class MyClass {

    ~MyClass();
    RunMyThread();

private:
    std::thread my_thread;
    std::atomic<bool> m_running;
}


MyClass::RunMyThread() {

    my_thread = std::thread { [this, m_running] {
    m_running = true;
    while(m_running) {
        std::this_thread::sleep_for(std::chrono::minutes(2));
        SendStatusInfo(some_info);
    }
}};
}

// Destructor
~MyClass::MyClass() {
    m_running = false; // this wont work as the thread is sleeping. How to exit thread here?
}
Run Code Online (Sandbox Code Playgroud)

问题:
这种方法的问题是我无法在线程休眠时退出线程.我从阅读中理解,我可以使用a唤醒它std::condition_variable并优雅地退出?但我正在努力寻找一个简单的例子,它可以满足上述场景中的要求.condition_variable我发现的所有例子看起来都太复杂了,我想在这里做些什么.

问题:
如何std::condition_variable在睡眠时使用a 唤醒线程并正常退出?或者,没有这种condition_variable技术,还有其他方法可以实现相同的目标吗?

另外,我看到我需要std::mutex结合使用std::condition_variable?这真的有必要吗?通过将std::condition_variable逻辑仅添加到代码中的所需位置,是否无法实现目标?

环境:
Linux和Unix与编译器gcc和clang.

Jon*_*ely 15

如何std::condition_variable唤醒线程并在睡眠时优雅地退出?或者没有condition_variable技术可以实现相同的其他方法吗?

不,不是在C++ 17的标准C++中(当然有非标准的,特定于平台的方法,并且可能会将某种信号量添加到C++ 2a中).

另外,我看到我需要std::mutex结合使用std::condition_variable?这真的有必要吗?

是.

通过将std::condition_variable逻辑仅添加到代码段中的所需位置,是否无法实现目标?

不可以.首先,您不能在condition_variable没有锁定互斥锁的情况下等待(并将锁定对象传递给等待函数),因此您无论如何都需要存在互斥锁.因为你必须有一个互斥体,要求服务员和通知者使用该互斥体并不是什么大问题.

条件变量受到"虚假唤醒"的影响,这意味着它们可以无缘无故地停止等待.为了告诉它是否因为被通知而醒来,或者虚假地醒来,你需要一些由通知线程设置并由等待线程读取的状态变量.因为该变量由多个线程共享,所以需要安全地访问它,这是互斥锁所确保的.

即使您使用原子变量作为共享变量,您仍然通常需要一个互斥锁来避免错过通知.

这些都在https://github.com/isocpp/CppCoreGuidelines/issues/554中有更详细的解释.

  • *"即使您使用原子变量作为共享变量,您仍然通常需要一个互斥锁以避免错过通知."*真正是使这成为最佳答案的原因.如果你有线程安全的情况,通常会认为你不需要互斥锁,这会导致可怕的头痛.编辑:似乎斯拉瓦的答案也提到了它. (2认同)

Sla*_*ica 8

我如何使用std :: condition_variable来唤醒线程并在它休眠时正常退出?

你使用std::condition_variable::wait_for()而不是std::this_thread::sleep_for()和第一个可以被std::condition_variable::notify_one()或中断std::condition_variable::notify_all()

另外,我看到我需要将std :: mutex与std :: condition_variable一起使用?这真的有必要吗?通过将std :: condition_variable逻辑仅添加到代码段中的所需位置,是否无法实现目标?

是的,它必须使用std::mutexstd::condition_variable和你应该使用它,而不是让你的旗帜std::atomic为尽管标志本身你就必须竞争条件在你的代码,你会发现,有时候你的睡眠线程将错过通知,如果你不会在这里使用互斥体的原子.


Max*_*kin 7

一个工作示例供您使用std::condition_variable:

struct MyClass {
    MyClass()
        : my_thread([this]() { this->thread(); })
    {}

    ~MyClass() {
        {
            std::lock_guard<std::mutex> l(m_);
            stop_ = true;
        }
        c_.notify_one();
        my_thread.join();
    }

    void thread() {
        while(this->wait_for(std::chrono::minutes(2)))
            SendStatusInfo(some_info);
    }

    // Returns false if stop_ == true.
    template<class Duration>
    bool wait_for(Duration duration) {
        std::unique_lock<std::mutex> l(m_);
        return !c_.wait_for(l, duration, [this]() { return stop_; });
    }

    std::condition_variable c_;
    std::mutex m_;
    bool stop_ = false;
    std::thread my_thread;
};
Run Code Online (Sandbox Code Playgroud)


Ser*_*eyA 5

有一个令人遗憾但却确实如此的事实 - 你正在寻找的是一个信号,并且Posix线程没有真正的信令机制.

此外,与任何时序相关联的唯一Posix线程原语是条件变量,这就是您的在线搜索引导您的原因,并且由于C++线程模型大量构建在Posix API上,因此在标准C++中Posix兼容原语就是你的全部.得到.

除非你愿意去Posix之外(你没有指出平台,但是有本机平台方式来处理那些没有这些限制的事件,特别是eventfd在Linux中),你将不得不坚持条件变量,是的,使用条件变量需要互斥锁,因为它内置于API中.

您的问题没有明确要求代码示例,所以我没有提供任何代码示例.如果你想要一些,请告诉我.