我正在尝试制作一个秒表类,该类具有一个单独的递减计数的线程。 这里的问题是,当我使用取消功能或尝试在超时后再次设置秒表时,程序崩溃。目前,我确定这是由于某些线程问题所致,可能是由于滥用所致。有没有人可以告诉我为什么它不起作用并可以帮助我使其起作用?
Stopwatch.h
class Stopwatch
{
public:
void Run(uint64_t ticks);
void Set(uint64_t ms);
void Cancel();
private:
std::thread mythread;
};
Stopwatch.cpp
void Stopwatch::Run(uint64_t ticks)
{
uint64_t clockCycles = ticks;
while(clockCycles > 0){
std::this_thread::sleep_for(std::chrono::milliseconds(1));
clockCycles--;
}
//do anything timeout related, probab a cout in the future
}
void Stopwatch::Set(uint64_t ms)
{
mythread = std::thread(&Timer::Run, this, ms);
}
void Stopwatch::Cancel()
{
mythread.join();
}
我想要的是调用秒表来设置时间并获得一些超时反应。使用取消功能可以随时停止。之后,使用set函数可以重新启动它。
答案 0 :(得分:1)
如注释中所述,您必须始终始终为可连接的join
调用detach
或std::thread
(即具有或具有关联的运行线程)。该代码无法在三个地方做到这一点。在Set
中,人们使用std::thread
的移动分配来分配给线程,而无需考虑其先前的内容。因此,多次调用Set
而不调用Cancel
可以保证终止。在Stopwatch
的移动分配运算符或析构函数中也没有这样的调用。
第二,因为Cancel
仅调用join
,所以它将在返回之前等待超时发生并执行,而不是取消计时器。要取消计时器,必须通知线程。另一方面,线程中的Run
循环必须有一种通知的方法。传统的方法是使用condition_variable。
例如:
class Stopwatch
{
public:
Stopwatch() = default;
Stopwatch(const Stopwatch&) = delete;
Stopwatch(Stopwatch&&) = delete;
Stopwatch& operator=(const Stopwatch&) = delete;
Stopwatch& operator=(Stopwatch&& other) = delete;
~Stopwatch()
{
Cancel();
}
void Run(uint64_t ticks)
{
std::unique_lock<std::mutex> lk{mutex_};
cv_.wait_for(lk, std::chrono::milliseconds(ticks), [&] { return canceled_; });
if (!canceled_)
/* timeout code here */;
}
void Set(uint64_t ms)
{
Cancel();
thread_ = std::thread(&Stopwatch::Run, this, ms);
}
void Cancel()
{
if (thread_.joinable())
{
{
std::lock_guard<std::mutex> lk{mutex_};
canceled_ = true;
}
cv_.notify_one();
thread_.join();
}
}
private:
bool canceled_ = false;
std::condition_variable cv_;
std::mutex mutex_;
std::thread thread_;
};