ASIO documentation for basic_deadline_timer::cancel()
有以下评论部分:
如果在调用
cancel()
时计时器已经过期,那么异步等待操作的处理程序将:
- 已被调用;要么
- 已经在不久的将来排队等待调用。
这些处理程序不能再被取消,因此传递一个错误代码,指示等待操作的成功完成。
我强调了这一点。通常,当您在计时器上调用cancel()
时,将使用错误代码“用户取消操作”运行回调。但是这表示用成功错误代码实际调用它的可能性很小。我认为它试图说可能发生以下情况:
async_wait(myTimerHandler)
,其中myTimerHandler()
是用户回调函数。io_context::post(cancelMyTimer)
,其中cancelMyTimer()
是用户回调函数。现在排队等待在线程A中调用。cancelMyTimer()
,在计时器上调用cancel()
。但是计时器已经被触发,并且ASIO没有检查处理程序是否仍然排队并且没有执行,所以这没有做任何事情。myTimerHandler
,并且在此期间不检查cancel()
是否被调用,因此它仍然成功通过错误代码。请记住,这个例子只有一个线程调用io_context::run()
,deadline_timer::async_wait
或deadline_timer::cancel()
。在另一个线程中发生的唯一事情是调用post()
,这是为了避免任何竞争条件。这一系列活动是否可行?或者它是指一些多线程场景(鉴于定时器不是线程安全的,这似乎不太可能)?
上下文:如果你有一个你希望定期重复的计时器,那么显而易见的事情就是检查回调中的错误代码,如果代码成功则再次设置计时器。如果可以进行上述竞赛,则需要有一个单独的变量来说明你是否取消了计时器,除了调用cancel()
之外你还更新了计时器。
你陈述的一切都是正确的。因此,在您的情况下,您可能需要一个单独的变量来指示您不想继续循环。我通常使用atomic_bool并且我不打扰发布取消例程,我只是设置bool和调用取消来自我所在的任何线程。
更新:
我的答案的来源主要是使用ASIO多年的经验以及了解asio代码库足以解决问题并在需要时扩展其部分内容。
是的文档说它在deadline_timer的共享实例之间不是线程安全的,但文档不是最好的(文档是什么......)。如果您查看“取消”如何工作的来源,我们可以看到:
Boost Asio版本1.69:boost \ asio \ detail \ impl \ win_iocp_io_context.hpp
template <typename Time_Traits>
std::size_t win_iocp_io_context::cancel_timer(timer_queue<Time_Traits>& queue,
typename timer_queue<Time_Traits>::per_timer_data& timer,
std::size_t max_cancelled)
{
// If the service has been shut down we silently ignore the cancellation.
if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
return 0;
mutex::scoped_lock lock(dispatch_mutex_);
op_queue<win_iocp_operation> ops;
std::size_t n = queue.cancel_timer(timer, ops, max_cancelled);
post_deferred_completions(ops);
return n;
}
您可以看到取消操作由互斥锁保护,因此“取消”操作是线程安全的。
在截止时间计时器上调用大多数其他操作不是(关于从多个线程同时调用它们)。
另外我认为你对快速排序的计时器重启是正确的。我通常没有用这种方式停止和启动计时器的用例,所以我从来不需要这样做。