【发布时间】:2015-09-21 10:37:07
【问题描述】:
我想要一个相当可靠的线程计时器,所以我编写了一个计时器对象,它在线程上触发 std::function。我想让这个计时器能够在它到达下一个刻度之前停止;你不能用 ::sleep 做的事情(至少我不认为你可以)。
所以我所做的就是在互斥体上放置一个条件变量。如果条件超时,我会触发该事件。如果条件发出信号,则线程退出。所以 Stop 方法需要能够让线程停止和/或中断它的等待,我认为这就是它现在正在做的事情。
然而,这存在一些问题。有时线程不是 joinable() ,有时条件是在在超时之后但在进入等待状态之前发出信号。
我怎样才能改进它并使它变得健壮?
以下是完整的回购。此处的等待时间为 10 秒,但程序应在创建 Foo 时立即终止,然后立即销毁。有时会,但大多数情况下不会。
#include <atomic>
#include <thread>
#include <future>
#include <sstream>
#include <chrono>
#include <iostream>
class Timer
{
public:
Timer() {}
~Timer()
{
Stop();
}
void Start(std::chrono::milliseconds const & interval, std::function<void(void)> const & callback)
{
Stop();
thread = std::thread([=]()
{
for(;;)
{
auto locked = std::unique_lock<std::mutex>(mutex);
auto result = terminate.wait_for(locked, interval);
if (result == std::cv_status::timeout)
{
callback();
}
else
{
return;
}
}
});
}
void Stop()
{
terminate.notify_one();
if(thread.joinable())
{
thread.join();
}
}
private:
std::thread thread;
std::mutex mutex;
std::condition_variable terminate;
};
class Foo
{
public:
Foo()
{
timer = std::make_unique<Timer>();
timer->Start(std::chrono::milliseconds(10000), std::bind(&Foo::Callback, this));
}
~Foo()
{
}
void Callback()
{
static int count = 0;
std::ostringstream o;
std::cout << count++ << std::endl;
}
std::unique_ptr<Timer> timer;
};
int main(void)
{
{
Foo foo;
}
return 0;
}
【问题讨论】:
-
您需要一个受互斥体保护的变量,该变量存储线程是否应该停止。条件变量是无状态的——你有责任维护你正在等待的事物的状态(称为“谓词”)。基本上,您错过了条件变量的要点。您的
Stop函数会通知线程。但它并没有改变线程正在等待的任何条件!请注意,互斥锁不保护任何东西。这是完全错误的。 -
更新了代码,因为我错过了 else...!
-
为什么
return会出现虚假唤醒? (同样的问题。您无法知道是否应该唤醒,因为您没有要检查的谓词。) -
还有哪些其他类型的唤醒?超时,用户发出等待信号。操作系统会因为其他原因唤醒它吗?
-
Ahhhhhhhh 我明白你现在对状态的意思了。我误解了 condition_variable 实际上在做什么。谢谢。
标签: c++ multithreading timer