Asio在包含处理程序的类被破坏后调用处理程序

Asio calls handler after the class containing the handler gets destructed

本文关键字:程序 处理 调用 包含 Asio      更新时间:2023-10-16

我有一个类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设置为"Success"。

有没有什么方法可以告诉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就可以判断对象是否已经被析构函数。这是一个肮脏的黑客,但目前有效。不过,从长远来看,我希望有一个更优雅的解决方案。

如果要确保对象在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*/)

解决方案是将X类成员声明为共享指针(即boost::shared_ptr<X> 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;
};