C++ 安全取消boost asio截止时间计时器

C++ 安全取消boost asio截止时间计时器,c++,multithreading,boost,timer,boost-asio,C++,Multithreading,Boost,Timer,Boost Asio,我正在尝试安全地取消一个boost::asio::basic\u waitable\u计时器 根据这一点,此代码应完成以下工作: timer.get_io_service().post([&]{timer.cancel();}) 恐怕这对我不起作用。 我做错什么了吗? 这是我的代码: #include <iostream> #include "boost/asio.hpp" #include <chrono> #include <thread> #i

我正在尝试安全地取消一个
boost::asio::basic\u waitable\u计时器

根据这一点,此代码应完成以下工作:

timer.get_io_service().post([&]{timer.cancel();})
恐怕这对我不起作用。
我做错什么了吗?
这是我的代码:

#include <iostream>
#include "boost/asio.hpp"
#include <chrono>
#include <thread>
#include <random>

boost::asio::io_service io_service;
boost::asio::basic_waitable_timer<std::chrono::steady_clock> timer(io_service);
std::atomic<bool> started;

void handle_timeout(const boost::system::error_code& ec)
{
    if (!ec) {
        started = true;
        std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout\n";
        timer.expires_from_now(std::chrono::milliseconds(10));
        timer.async_wait(&handle_timeout);
    } else if (ec == boost::asio::error::operation_aborted) {
        std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout aborted\n";
    } else {
        std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout another error\n";
    }
}

int main() {

    std::cout << "tid: " << std::this_thread::get_id() << ", Hello, World!" << std::endl;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 100);

    for (auto i = 0; i < 1000; i++) {

        started = false;
        std::thread t([&](){

            timer.expires_from_now(std::chrono::milliseconds(0));
            timer.async_wait(&handle_timeout);

            io_service.run();
        });

        while (!started) {};
        auto sleep = dis(gen);
        std::cout << "tid: " << std::this_thread::get_id() << ", i: " << i << ", sleeps for " << sleep << " [ms]" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
        timer.get_io_service().post([](){
            std::cerr << "tid: " << std::this_thread::get_id() << ", cancelling in post\n";
            timer.cancel();
        });
//      timer.cancel();
        std::cout << "tid: " << std::this_thread::get_id() << ", i: " << i << ", waiting for thread to join()" << std::endl;
        t.join();
        io_service.reset();
    }

    return 0;
}
#包括
#包括“增压/asio.hpp”
#包括
#包括
#包括
boost::asio::io_服务io_服务;
boost::asio::基本可等待计时器(io服务);
std::原子启动;
无效句柄超时(const boost::system::error\u code&ec)
{
如果(!ec){
开始=真;
取消是安全的

它只是不够健壮。您没有考虑计时器未挂起的情况。您可以取消一次计时器,但一旦调用完成处理程序,它将启动一个新的异步等待

下面是我追踪问题的详细步骤

摘要TL;DR

取消时间只会取消飞行中的异步操作

如果要关闭异步调用链,则必须使用其他逻辑。下面给出了一个示例

处理程序跟踪 使能

#define BOOST_ASIO_ENABLE_HANDLER_TRACKING 1
这将生成可以用boost/libs/asio/tools/handlerviz.pl可视化的输出:

成功的追踪

如您所见,当取消发生时,
async\u wait
正在运行中

“坏”痕迹 (被截断,因为它将无限运行)

请注意完成处理程序如何看待
cc=system:0
,而不是
cc=system:125
(对于
操作_aborted
)。这是发布的取消实际上没有“执行”的症状。唯一的逻辑解释(在图表中不可见)是,在调用取消之前计时器已经过期

让我们比较一下原始痕迹

⑨消除噪音差异

检测它 我们有线索了,我们能发现吗

    timer.get_io_service().post([](){
        std::cerr << "tid: " << std::this_thread::get_id() << ", cancelling in post\n";
        if (timer.expires_from_now() >= std::chrono::steady_clock::duration(0)) {
            timer.cancel();
        } else {
            std::cout << "PANIC\n";
            timer.cancel();
        }
    });
我们可以用另一种更清晰的方式来传达“超级取消”吗?我们只有一个
定时器
对象可以使用,当然:

信号关闭
timer
对象没有太多的属性可供使用。没有可用于将计时器置于某种无效状态的
close()
或类似对象,如套接字上的

但是,有到期时间点,我们可以使用一个特殊的域 值表示我们的应用程序“无效”:

timer.get_io_service().post([](){
    std::cerr << "tid: " << std::this_thread::get_id() << ", cancelling in post\n";
    // also cancels:
    timer.expires_at(Timer::clock_type::time_point::min());
});
timer.get\u io\u service().post([]){

哇,谢谢!你写了
取消是安全的。
-你的意思是使用
post()
,对吗?不是普通的
计时器。取消()
?很好的解决方法,但是…你不认为应该有更好的取消功能来隐藏其实现细节中的所有混乱吗?取消相关的问题一次又一次出现…@hudac我只是确认你使用它是线程安全的,实际上我没有说任何其他内容。你使用它是安全的,因为你将它发布到服务和服务在一个线程上运行,这意味着您获得了“隐式串”行为(没有两个处理程序同时运行)。@hudac更具体地说,一旦您在更多线程上运行服务,这就不是经验法则!在这种情况下,您需要一个串来同步对服务对象的访问(如
deadline\u timer
)。请看。我希望这能让大家明白,根据文档(没有人这么说),
cancel()
不是线程安全的。@hudac我不这么认为(我通常只听一次INT/TERM)。当然,你可以简单地(现在当你得到信号0时,这意味着你可能应该关机)
timer.get_io_service().post([](){
    std::cerr << "tid: " << std::this_thread::get_id() << ", cancelling in post\n";
    // also cancels:
    timer.expires_at(Timer::clock_type::time_point::min());
});
void handle_timeout(const boost::system::error_code& ec)
{
    if (!ec) {
        started = true;
        if (timer.expires_at() != Timer::time_point::min()) {
            timer.expires_from_now(std::chrono::milliseconds(10));
            timer.async_wait(&handle_timeout);
        } else {
            std::cerr << "handle_timeout: detected shutdown\n";
        }
    } 
    else if (ec != boost::asio::error::operation_aborted) {
        std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout error " << ec.message() << "\n";
    }
}