【问题标题】:Asio calls handler after the class containing the handler gets destructed在包含处理程序的类被破坏后,Asio 调用处理程序
【发布时间】:2015-05-25 23:20:36
【问题描述】:

我有一个类 X,它在其构造函数中引用 boost::asio::io_service 和一个连接的 boost::astio::ip::tcp::socket。该类处理网络数据的发送和接收。

我遇到的一个问题是主代码要求 X 发送消息 X::sendMessage(),然后主代码在下一行删除 X。因此,X 调用boost::asio::async_write,使用 lambda 作为处理程序,然后 X 被删除,它的析构函数被调用,从而关闭套接字。没有X了。但过了一会儿,boost::asio::io_service 调用了我在 boost::asio::async_write 调用中使用的 lambda 处理程序,它现在在一个被破坏的类中,boost::system::error_code 设置为“成功”。

有没有办法告诉 lambda 该类已被破坏,它不应该与类的成员和方法混淆?

也许我可以以某种方式取消在 ~X() 中调用的 lambda 处理程序?虽然写操作可能已经完成并安排处理程序由boost::asio::io_service 执行,所以没有什么可以取消。

请注意,我不能对 boost::asio::io_service 对象做任何事情,因为它是由主代码传递给 X 的,它处理的不仅仅是网络。

class X
{
public:
    X(boost::asio::io_service &io, boost::asio::ip::tcp::socket socket)
    : io(io), socket(std::move(socket)){ }

    ~X()
    {
        if (socket.is_open()) {
            socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
            socket.close();
        }
    }

    void X::sendMessage()
    {
        boost::asio::async_write(socket, boost::asio::buffer(m.data, m.size),
            [this](boost::system::error_code ec, std::size_t /*length*/)
            {
                std::cout << ec.message() << std::endl; // Success!
                // `this' is invalid though

                if (!ec) {
                    // code
                } else {
                   // code
                }
            });
    }

private:
    boost::asio::io_service &io;
    boost::asio::ip::tcp::socket socket;

编辑:

现在我刚刚创建了一个带有 bool 的共享指针,我将它传递给 lambdas 并在析构函数中设置为 true,以便 lambdas 可以判断对象是否已经被破坏。这是一个肮脏的黑客,但它现在有效。不过,从长远来看,我希望有一个更优雅的解决方案。

【问题讨论】:

    标签: c++ c++11 boost lambda boost-asio


    【解决方案1】:

    如果您想确保您的对象在asio 框架有对它的引用时保持活动状态,请将指向该对象的共享所有权智能指针传递给 lambda 表达式。例如:

    class X : public enable_shared_from_this<X>
    // ...
    
        boost::shared_ptr<X> that = this->shared_from_this();
        boost::asio::async_write(socket, boost::asio::buffer(m.data, m.size),
            [that](boost::system::error_code ec, std::size_t /*length*/)
    

    【讨论】:

      【解决方案2】:

      解决方案是将 X 类成员声明为共享指针(即boost::shared_ptr&lt;X&gt; m_x;)并将类实例传递给 lambda。请参阅下面的修改代码。

      在 Asio 调用 lambda 之前,它会阻止类实例销毁。

      #include <boost/noncopyable.hpp>
      #include <boost/enable_shared_from_this.hpp>
      #include <boost/asio.hpp>
      
      class X : private boost::noncopyable, public boost::enable_shared_from_this<X>
      {
      private:
          struct Data {
              char *data;
              size_t size;
          };
          Data m;
      public:
          X(boost::asio::io_service &io, boost::asio::ip::tcp::socket socket)
              : io(io), socket(std::move(socket)){ }
      
          ~X()
          {
              if (socket.is_open()) {
                  socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
                  socket.close();
              }
          }
      
          void X::sendMessage()
          {
              auto self = shared_from_this();
              boost::asio::async_write(socket, boost::asio::buffer(m.data, m.size),
                  [self](boost::system::error_code ec, std::size_t /*length*/)
              {
                  std::cout << ec.message() << std::endl; // Success!
                  // `this' is invalid though
      
                  if (!ec) {
                      // code
                  }
                  else {
                      // code
                  }
              });
          }
      
      private:
          boost::asio::io_service &io;
          boost::asio::ip::tcp::socket socket;
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-22
        • 2015-02-14
        • 1970-01-01
        • 2023-02-21
        • 1970-01-01
        相关资源
        最近更新 更多