当任务所有者被销毁时,取消挂起的任务回调调用

Cancel pending task callback invocation when the task owner is destructed

本文关键字:任务 挂起 取消 回调 调用 所有者      更新时间:2023-10-16

我正在实现一个看起来像HTTP服务器的东西,设计是:对于一个已经建立的连接,我想在几个请求中重用它,所以当一个请求完成时,我用async_read启动另一个读取任务,同时启动一个deadline_timer。如果60秒内没有输入,计时器将被触发,连接将被破坏。让我恼火的是,在调用连接的析构函数之前,我们设置为async_read的回调将被调用。

所以,我的问题是,有没有任何方法可以取消挂起的读取任务,即在不调用回调函数的情况下破坏连接?

如果上面的一般描述不清楚,详细的工作流程如下(代码附在底部):

请求完成时调用cleanup()
  • 在CCD_ 5中启动定时器和另一个读取任务
  • 如果超时,则调用HandleTimeout(),并且它调用stop()
  • stop()中,进行清理工作,之后连接实例将被销毁
  • 但是,在步骤4之后,将调用callback()函数,该函数已在AsyncRead()中注册,那么,有什么方法可以取消对callback()的调用吗?

    代码:

    class Connection : public boost::enable_shared_from_this<Connection>,
    private boost::noncopyable {
    public:
    typedef Connection this_type;
    void cleanup() {
    timer_.expires_from_now(boost::posix_time::seconds(kDefaultTimeout));
    timer_.async_wait(boost::bind(&this_type::HandleTimeout,
    shared_from_this(),
    boost::asio::placeholders::error));
    AsyncRead();
    }
    void AsyncRead() {
    boost::asio::async_read(*socket_, in_, boost::asio::transfer_at_least(1),
    boost::bind(&this_type::callback,
    shared_from_this(),
    boost::asio::placeholders::error));
    }
    void callback(const boost::system::error_code& e) {
    // ...
    }
    void HandleTimeout(const boost::system::error_code& e) {
    if(e == boost::asio::error::operation_aborted)
    LDEBUG << "The timeout timer is cancelled.";
    else if(e)
    LERROR << "Error occurred with the timer, message: " << e.message();
    else if(timer_.expires_at()
    <= boost::asio::deadline_timer::traits_type::now()) {
    LDEBUG << "Connection timed out, close it.";
    stop();
    }
    }
    virtual void stop() {
    connected_ = false;
    socket_->close();
    connection_manager_.stop(shared_from_this());
    }
    private:
    // ...
    boost::asio::deadline_timer timer_;
    };
    

    没有干净的方法可以实现这一点。确保不会调用准备运行的处理程序(如Connection::callback())的唯一方法是:

    • 停止处理io_service事件循环
    • 销毁io_service,因为io_service的析构函数将导致销毁所有未完成的处理程序

    在示例代码中,如果套接字不再打开,请考虑在Connection::callback()中返回:

    void callback(const boost::system::error_code& error)
    {
    if (!socket_.is_open()) return;
    // ...
    }
    

    还要注意,error_code参数不足以推断是否发生了超时。当socket::close()被调用时,Connection::callback()可能被排队等待boost::system::errc::successerror_code的调用。因此,没有可取消的操作。