Timer ASIO定时器'cancel()'可以调用一个伪'quot;成功;?
本节的备注如下: 如果调用Timer ASIO定时器'cancel()'可以调用一个伪'quot;成功;?,timer,boost-asio,Timer,Boost Asio,本节的备注如下: 如果调用cancel()时计时器已过期,则异步等待操作的处理程序将: 已经被调用;或 已排队等待在不久的将来调用 无法再取消这些处理程序,因此,会向它们传递一个错误代码,指示等待操作已成功完成 我补充了重点。通常,当您在计时器上调用cancel()时,回调运行时会出现错误代码“操作被用户取消”。但这表明,它很有可能被成功的错误代码调用。我认为这是在说可能发生以下情况: 线程A在计时器上调用async\u wait(myTimerHandler),其中myTimerHandl
cancel()
时计时器已过期,则异步等待操作的处理程序将:
- 已经被调用;或
- 已排队等待在不久的将来调用
cancel()
时,回调运行时会出现错误代码“操作被用户取消”。但这表明,它很有可能被成功的错误代码调用。我认为这是在说可能发生以下情况:
async\u wait(myTimerHandler)
,其中myTimerHandler()
是一个用户回调函数io\U上下文::post(cancelMyTimer)
其中cancelMyTimer()
是一个用户回调函数。现在,它已排队等待在线程A中调用cancelMyTimer()
,线程A在计时器上调用cancel()
。但计时器已经启动,ASIO不会检查处理程序是否仍在排队且未执行,因此这不会执行任何操作myTimerHandler
,并且不检查同时调用了cancel()
,因此它仍然将success作为错误代码传递io\u context::run()
,deadline\u timer::async\u wait
或deadline\u timer::cancel()
。在另一个线程中发生的唯一事情是调用post()
,这是为了避免任何竞争条件。这一系列事件可能发生吗?或者它是指一些多线程场景(考虑到计时器不是线程安全的,这似乎不太可能)
上下文:如果您希望定期重复一个计时器,那么最明显的做法是检查回调中的错误代码,如果代码成功,则再次设置计时器。如果上述竞争是可能的,那么就需要有一个单独的变量来说明是否取消了计时器,除了调用
cancel()
之外,还需要更新计时器 你所说的一切都是正确的。因此,在您的情况下,您可能需要一个单独的变量来表示您不想继续循环。我通常使用一个原子布尔,我不需要发布取消例程,我只是从我所在的线程设置布尔&调用取消
更新:
我的答案的来源主要是多年来使用ASIO的经验,以及对ASIO代码库的足够理解,以便在需要时修复问题并扩展部分ASIO
是的,文档中说,在deadline_timer的共享实例之间不是线程安全的,但是文档不是最好的(什么文档是…)。如果您查看源代码了解“取消”是如何工作的,我们可以看到:
Boost Asio版本1.69:Boost\Asio\detail\impl\win\u iocp\u io\u 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;
}
模板
std::size\u t win\u iocp\u io\u上下文::取消计时器(计时器队列和队列,
typename timer_queue::per_timer_data&timer,
标准::尺寸(最大值已取消)
{
//如果服务已经关闭,我们会默默地忽略取消。
如果(::InterlocatedExchangeAdd(&shutdown,0)!=0)
返回0;
互斥锁:作用域锁定锁(分派互斥锁);
队列操作;
std::size\u t n=队列。取消计时器(计时器、操作、最大值已取消);
延期竣工后(ops);
返回n;
}
您可以看到取消操作由互斥锁保护,因此“取消”操作是线程安全的
在截止时间计时器上调用大多数其他操作是不正确的(关于从多个线程同时调用它们)
此外,我认为你是正确的重新启动定时器在快速秩序。我通常没有以这种方式停止和启动计时器的用例,所以我从来没有必要这样做。您甚至不需要第二个线程来运行
basic_waitable_timer::cancel()
调用太晚的情况(因为计时器的(完成)处理程序已经排队)
对于尚未恢复的basic_waitable_timer::async_wait()
,程序可以同时执行一些其他异步操作。如果然后仅依靠basic_waitable_timer::cancel()
进行取消,则来自另一个异步(完成)处理程序的cancel()
调用将与已调度的async_wait()
处理程序竞争:
如果调用cancel()时计时器已过期,则异步等待操作的处理程序将:
- 已经被调用;或
- 已排队等待在不久的将来调用。
io_server.run()
),其中包含所描述的竞争:
void Fetch_Timer::resume()
{
timer_.expires_from_now(std::chrono::seconds(1));
timer_.async_wait([this](const boost::system::error_code &ec)
{
BOOST_LOG_FUNCTION();
if (ec) {
if (ec.value() == boost::asio::error::operation_aborted)
return;
THROW_ERROR(ec);
} else {
print();
resume();
}
});
}
void Fetch_Timer::stop()
{
print();
timer_.cancel();
}
(来源:)
在本例中,由于程序是单线程的,所以(也就是查询布尔标志)甚至不需要使用任何同步原语(如原子)。这意味着它并行执行(异步)操作,但不是并行执行
(FWIW,在上述示例中,b