Timer ASIO定时器'cancel()'可以调用一个伪'quot;成功;?

Timer ASIO定时器'cancel()'可以调用一个伪'quot;成功;?,timer,boost-asio,Timer,Boost Asio,本节的备注如下: 如果调用cancel()时计时器已过期,则异步等待操作的处理程序将: 已经被调用;或 已排队等待在不久的将来调用 无法再取消这些处理程序,因此,会向它们传递一个错误代码,指示等待操作已成功完成 我补充了重点。通常,当您在计时器上调用cancel()时,回调运行时会出现错误代码“操作被用户取消”。但这表明,它很有可能被成功的错误代码调用。我认为这是在说可能发生以下情况: 线程A在计时器上调用async\u wait(myTimerHandler),其中myTimerHandl

本节的备注如下:

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

  • 已经被调用;或
  • 已排队等待在不久的将来调用
无法再取消这些处理程序,因此,会向它们传递一个错误代码,指示等待操作已成功完成

我补充了重点。通常,当您在计时器上调用
cancel()
时,回调运行时会出现错误代码“操作被用户取消”。但这表明,它很有可能被成功的错误代码调用。我认为这是在说可能发生以下情况:

  • 线程A在计时器上调用
    async\u wait(myTimerHandler)
    ,其中
    myTimerHandler()
    是一个用户回调函数
  • 线程B调用
    io\U上下文::post(cancelMyTimer)
    其中
    cancelMyTimer()
    是一个用户回调函数。现在,它已排队等待在线程A中调用
  • 计时器截止日期过期,因此ASIO将计时器回调处理程序排队,并显示一个成功错误代码。它还没有调用它,但它已排队等待在线程A中调用
  • ASIO开始在线程A中调用
    cancelMyTimer()
    ,线程A在计时器上调用
    cancel()
    。但计时器已经启动,ASIO不会检查处理程序是否仍在排队且未执行,因此这不会执行任何操作
  • 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