在另一个线程中原子地取消asio异步定时器

5

我有一个定期运行的boost deadline_timer(如示例中所示http://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tutorial/tuttimer3/src.html):

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t)
{
    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
    t->async_wait(boost::bind(print,
          boost::asio::placeholders::error, t, count));
}

int main()
{
  boost::asio::io_service io;

  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait(boost::bind(print,
        boost::asio::placeholders::error, &t));

  io.run();

  return 0;
}

现在我需要从另一个线程取消它。但是如果取消调用出现在打印函数执行期间但在expires_at调用之前怎么办?那么定时器将继续运行。
解决这个问题的一种方法是运行类似于以下内容的东西:
while (timer.cancel() == 0) {
}

在那个单独的线程函数中。
但也许有人知道更优雅的方法来解决这个问题?
1个回答

4

实际上,这两种方法都不是非常安全,因为deadline_timer不是线程安全的(参见此处)

我认为,最简单和安全的方法是使用post取消操作:

//...
timer.get_io_service().post([&]{timer.cancel();})
//...

注意: 实际代码中需要确保 timer 的生命周期比函数对象 (lambda) 长。
更新: 如 @sehe 所提到的,这种解决方案可能无法正常工作,因为取消处理程序可能会在计时器不再等待时出现在 io_service 队列中,正好在 print 之前。

3
请注意,这可能存在竞态条件(发布的 cancel() 可能发生在 async_await 不处于挂起状态的时候)。请参见例如 https://dev59.com/nFgQ5IYBdhLWcg3wMg9J。 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接