timer - ASIO 计时器 `cancel()` 可以调用虚假的“成功”吗?
问题描述
ASIO 文档basic_deadline_timer::cancel()
有以下备注部分:
如果定时器在被调用时已经过期
cancel()
,那么异步等待操作的处理程序将:
- 已经被调用;或者
- 已在不久的将来排队等待调用。
这些处理程序不能再被取消,因此会传递一个指示等待操作成功完成的错误代码。
重点是我加的。通常,当您调用cancel()
计时器时,会运行回调并显示“用户取消操作”的错误代码。但这表明它实际上可以用成功错误代码调用的可能性很小。我认为它试图说可能会发生以下情况:
- 线程 A 调用
async_wait(myTimerHandler)
计时器,其中myTimerHandler()
是用户回调函数。 - 线程 B 调用
io_context::post(cancelMyTimer)
wherecancelMyTimer()
是一个用户回调函数。现在排队等待在线程 A 中调用。 - 定时器期限到期,因此 ASIO 将定时器回调处理程序排队,并带有成功错误代码。它还没有调用它,但它已排队等待在线程 A 中调用。
- ASIO 开始调用
cancelMyTimer()
线程 A,它调用cancel()
计时器。但是计时器已经触发,并且 ASIO 不会检查处理程序是否仍在排队并且没有执行,所以这什么都不做。 - 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;
}
您可以看到取消操作由互斥锁保护,因此“取消”操作是线程安全的。
在截止日期计时器上调用大多数其他操作不是(关于从多个线程同时调用它们)。
另外,我认为您对快速重新启动计时器的看法是正确的。我通常没有以这种方式停止和启动计时器的用例,所以我从来不需要这样做。
推荐阅读
- r - Shiny 承诺未来不适用于 eventReactive
- msp430 - 运行 -> 调试未出现在 Code Composer Studio 中
- angular - CakePHP 3 和 Angular 6 的常用加密/解密技术是什么?
- python - 在 PyMC3 Flask 应用程序中处理多个请求
- rpa - Uipath:机器人状态总是断开连接
- ios - 如何做 json 类型的响应?
- javascript - 如何使用 jquery 在嵌套数组中显示数据?
- python - 在 pandas 中找出并记录失败的验证条件
- android - 将 Dagger Module 类中的静态方法转换为 Kotlin
- sphinx - 狮身人面像 3.1.1。DocStore 似乎失败了