ASIO 计时器`cancel()` 可以调用虚假的“成功"吗? [英] Can ASIO timer `cancel()` call a spurious "success"?

查看:44
本文介绍了ASIO 计时器`cancel()` 可以调用虚假的“成功"吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ASIO 文档对于 basic_deadline_timer::cancel() 有以下备注部分:

The ASIO documentation for basic_deadline_timer::cancel() has the following remarks section:

如果在调用 cancel() 时计时器已经到期,则异步等待操作的处理程序将:

If the timer has already expired when cancel() is called, then the handlers for asynchronous wait operations will:

  • 已经被调用;或
  • 已在不久的将来排队等待调用.

这些处理程序无法再取消,因此传递了一个错误代码,指示等待操作成功完成.

These handlers can no longer be cancelled, and therefore are passed an error code that indicates the successful completion of the wait operation.

重点是我加的.通常,当您在计时器上调用 cancel() 时,会运行回调并带有用户取消操作"的错误代码.但这表示实际上可以使用成功错误代码调用它的可能性很小.我认为它试图说明以下情况可能发生:

The emphasis has been added by me. Normally when you call cancel() on a timer, the callback is run with an error code of "operation cancelled by user". But this says there is a small chance it could actually be called with a success error code. I think it is trying to say that the following could happen:

  1. 线程 A 在计时器上调用 async_wait(myTimerHandler),其中 myTimerHandler() 是用户回调函数.
  2. 线程 B 调用 io_context::post(cancelMyTimer),其中 cancelMyTimer() 是用户回调函数.现在排队等待在线程 A 中调用.
  3. 计时器截止时间到期,因此 ASIO 将计时器回调处理程序与成功错误代码一起排队.它还没有调用它,而是在线程A中排队等待调用.
  4. ASIO 开始在线程 A 中调用 cancelMyTimer(),该线程在计时器上调用 cancel().但是计时器已经触发,并且 ASIO 不会检查处理程序是否仍在排队且未执行,因此这什么都不做.
  5. ASIO 现在调用 myTimerHandler,并且不会检查在此期间调用了 cancel(),因此它仍然将成功作为错误代码传递.立>
  1. Thread A calls async_wait(myTimerHandler) on a timer, where myTimerHandler() is a user callback function.
  2. Thread B calls io_context::post(cancelMyTimer) where cancelMyTimer() is a user callback function. This is now queued up to be called in thread A.
  3. The timer deadline expires, so ASIO queues up the timer callback handler, with a success error code. It doesn't call it yet, but it is queued up to be called in thread A.
  4. ASIO gets round to calling cancelMyTimer() in thread A, which calls cancel() on the timer. But the timer already fired, and ASIO doesn't check that the handler is still queued up and not executed, so this does nothing.
  5. ASIO now calls myTimerHandler, and doesn't check that cancel() was called in the meantime, and so it still passes success as the error code.

记住这个例子只有一个线程调用 io_context::run()deadline_timer::async_waitdeadline_timer::cancel().在另一个线程中发生的唯一事情是对 post() 的调用,这是为了避免任何竞争条件而发生的.这一系列事件可能吗?或者它指的是一些多线程场景(鉴于定时器不是线程安全的,这似乎不太可能)?

Bear in mind this example only has a single thread calling io_context::run(), deadline_timer::async_wait or deadline_timer::cancel(). The only thing that happened in another thread was a call to post(), which happened in an attempt to avoid any race conditions. Is this sequence of events possible? Or is it referring to some multithreading scenario (that seems unlikely given that timers are not thread safe)?

上下文:如果您有一个希望定期重复的计时器,那么显而易见的事情就是检查回调中的错误代码,如果代码成功,则再次设置计时器.如果上述竞争是可能的,那么就需要有一个单独的变量来说明您是否取消了计时器,除了调用 cancel() 之外,您还要更新它.

Context: If you have a timer that you wish to repeat periodically, then the obvious thing to do is check the error code in the callback, and set the timer again if the code is success. If the above race is possible, then it would be necessary to have a separate variable saying whether you cancelled the timer, which you update in addition to calling cancel().

推荐答案

您所说的一切都是正确的.因此,在您的情况下,您可能需要一个单独的变量来指示您不想继续循环.我通常使用 atomic_bool 并且我不费心发布取消例程,我只是设置 bool &从我所在的任何线程调用取消.

Everything you stated is correct. So in your situation you could need a separate variable to indicate you don’t want to continue the loop. I normally used a atomic_bool and I don’t bother posting a cancel routine, I just set the bool & call cancel from whatever thread I am on.

更新:

我的答案主要来自多年来使用 ASIO 的经验,以及对 asio 代码库的足够了解,以便在需要时修复问题并扩展部分代码.

The source for my answer is mainly experience in using ASIO for years and for understanding the asio codebase enough to fix problems and extend parts of it when required.

是的,文档说它在deadline_timer 的共享实例之间不是线程安全的,但是文档不是最好的(什么文档是......).如果您查看取消"如何工作的来源,我们可以看到:

Yes the documentation says that the it's not thread safe between shared instances of the deadline_timer, but the documentation is not the best (what documentation is...). If you look at the source for how the "cancel" works we can see:

Boost Asio 1.69 版:boost\asio\detail\impl\win_iocp_io_context.hpp

Boost Asio version 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;
}

您可以看到取消操作由互斥锁保护,因此取消"操作是线程安全的.

You can see that the cancel operation is guarded by a mutex lock so the "cancel" operation is thread safe.

在截止时间计时器上调用大多数其他操作不是(关于从多个线程同时调用它们).

Calling most of the other operations on deadline timer is not (in regards to calling them at the same time from multiple threads).

此外,我认为您对快速重启计时器的看法是正确的.我通常没有以这种方式停止和启动计时器的用例,所以我从来不需要这样做.

Also I think you are correct about the restarting of timers in quick order. I don't normally have a use case for stopping and starting timers in that sort of fashion, so I've never needed to do that.

这篇关于ASIO 计时器`cancel()` 可以调用虚假的“成功"吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆