【问题标题】:Cancel pending task callback invocation when the task owner is destructed销毁任务所有者时取消挂起的任务回调调用
【发布时间】:2013-12-21 09:10:42
【问题描述】:

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

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

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

  1. cleanup() 在请求完成时被调用;
  2. 在cleanup()中启动定时器和另一个阅读任务;
  3. 如果超时,则调用HandleTimeout(),并调用stop();
  4. 在stop()中,做clean工作,之后连接实例会被销毁。

但是,在第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_;
};

【问题讨论】:

    标签: c++ boost-asio


    【解决方案1】:

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

    • 停止处理io_service 事件循环。
    • Destroyio_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::success 的error_code 排队等待调用。因此,没有可取消的操作。

    【讨论】:

    • 感谢您的回答,我只是想确认是否有办法做到这一点,现在答案似乎是“否”,是的,我需要一个解决方法来做到这一点。
    • @KelvinHu 你能详细说明为什么你需要不同的行为吗?如果在Connection 销毁之前调用callback() 只是烦您,那么请考虑通过boost::weak_ptr 将Connection 的生命周期与回调处理程序解耦。这个answer 演示了一个基本的弱指针绑定器,这个answer 显式地将weak_ptr 传递给一个处理程序。在任一解决方案中,都会调用处理程序,但仅当Connection 处于活动状态时才会调用callback()。
    猜你喜欢
    • 2018-03-14
    • 2017-10-22
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 2020-10-22
    • 2015-10-13
    • 2021-12-14
    • 2013-03-27
    相关资源
    最近更新 更多