作者:秋静222 | 来源:互联网 | 2022-12-03 12:10
我正在使用C++ 11,我有一个std::thread
类成员,它每2分钟向听众发送一次信息.其他它只是睡觉.所以,我让它睡了2分钟,然后发送所需的信息,然后再睡2分钟.
// MyClass.hpp
class MyClass {
~MyClass();
RunMyThread();
private:
std::thread my_thread;
std::atomic 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?
}
问题:
这种方法的问题是我无法在线程休眠时退出线程.我从阅读中理解,我可以使用a唤醒它std::condition_variable
并优雅地退出?但我正在努力寻找一个简单的例子,它可以满足上述场景中的要求.condition_variable
我发现的所有例子看起来都太复杂了,我想在这里做些什么.
问题:
如何std::condition_variable
在睡眠时使用a 唤醒线程并正常退出?或者,没有这种condition_variable
技术,还有其他方法可以实现相同的目标吗?
另外,我看到我需要std::mutex
结合使用std::condition_variable
?这真的有必要吗?通过将std::condition_variable
逻辑仅添加到代码中的所需位置,是否无法实现目标?
环境:
Linux和Unix与编译器gcc和clang.
1> Jonathan Wak..:
如何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> Slava suppor..:
我如何使用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::mutex
与std::condition_variable
和你应该使用它,而不是让你的旗帜std::atomic
为尽管标志本身你就必须竞争条件在你的代码,你会发现,有时候你的睡眠线程将错过通知,如果你不会在这里使用互斥体的原子.
3> Maxim Egorus..:
一个工作示例供您使用std::condition_variable
:
struct MyClass {
MyClass()
: my_thread([this]() { this->thread(); })
{}
~MyClass() {
{
std::lock_guard 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
bool wait_for(Duration duration) {
std::unique_lock 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;
};
4> SergeyA..:
有一个令人遗憾但却确实如此的事实 - 你正在寻找的是一个信号,并且Posix线程没有真正的信令机制.
此外,与任何时序相关联的唯一Posix线程原语是条件变量,这就是您的在线搜索引导您的原因,并且由于C++线程模型大量构建在Posix API上,因此在标准C++中Posix兼容原语就是你的全部.得到.
除非你愿意去Posix之外(你没有指出平台,但是有本机平台方式来处理那些没有这些限制的事件,特别是eventfd
在Linux中),你将不得不坚持条件变量,是的,使用条件变量需要互斥锁,因为它内置于API中.
您的问题没有明确要求代码示例,所以我没有提供任何代码示例.如果你想要一些,请告诉我.