异步I / O操作 - 避免删除对象的正确方法

时间:2016-03-06 10:48:24

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

使用来自boost::asio的异步I / O操作我经常需要共享指针(以及回调中的enable_shared_from_thisshared_from_this)以避免过早删除对象。 我认为可以通过在课堂上保留unique_ptr或只是对象(所有权)(作为成员)来实现

例如: 只有foo方法使用sender

第1(流行解决方案):

class C {
    public:
    void foo
    {
        std::shared_ptr<Sender> sender = std::make_shared<Sender>();
        sender->send(); 
        // class Sender use async_write
        // inheritance: enable_shared_from_this
        // callback in async operation created with argument shared_from_this
    }
};

为什么没有人(?)使用此解决方案:

class D {
public:
    void foo
    {
        sender.reset(new Sender);
        sender->send(); 
    }
private:
    std::unique_ptr<Sender> sender;
};

我知道发件人不会被提前删除。我没有shared_ptrs。我认为避免使用它们是件好事,因为如果Sender类中的回调也使用异步操作,我需要另一个共享指针等。我认为D类更易于阅读。 但我想知道它是不是一个好的风格......我总是在网上看到与shared_ptrs的解决方案。

3 个答案:

答案 0 :(得分:0)

对象只能有一个unique_ptr。如果您同时发送和接收操作都会发生什么?如果您同时拥有接收操作和计时器会发生什么?

是的,您可以使用unique_ptr,但是您必须实施自己的未完成操作数量计数。您必须以线程安全的方式递增和递减该计数。并且您必须不断检查该计数是否为零以调用析构函数。要清理它,您可能会将所有这些功能捆绑到自己的类中。而且你已经彻底改造了shared_ptr

答案 1 :(得分:0)

后一种用法很脆弱,无法保证不会过早删除发件人。例如,考虑在异步操作完成之前连续调用D::foo()的情况。可以使用可能取决于Sender对象的未完成操作删除先前的Sender对象:

public: Sender
{
public:
  void send()
  {
    boost::asio::async_write(socket_, boost::asio::buffer(buffer_), ...);
  }
private:
  std::array<char, 64> buffer_;
  boost::asio::ip::tcp::socket socket_;
  ...
};

D d;
d.foo(); // Create Sender1, initiate async_write.
d.foo(); // Delete Sender1, create Sender2, initiate async_write.
io_service.run(); // Sender1's async_write invokes undefined behavior.

在上面的代码中,对d.foo()的第二次调用将删除具有未完成操作的第一个Sender对象,从而导致未定义的行为。另一方面,如果Sender继承自enable_shared_from_this,并且shared_from_this()的结果绑定到异步操作的处理程序,那么Sender对象的生命周期会延长至少与操作一样长。

答案 2 :(得分:0)

你看过asio :: spawn吗?基本上,它消除了异步C ++编程的大部分复杂性,并允许您将对象放在堆栈上。

以下是一个简单示例:modern.cpp

使用这种方法可以节省大量实现和调试异步应用程序的时间,并且使代码非常容易理解。 (我曾经使用传统的回调在TCP代理上实现了RTP。事后证明这是一个噩梦。)

关于分片与唯一指针 - 我的经验是共享指针更易于与asio一起使用。移动独特的指针存在一些问题,当程序变得稍微复杂时,很容易犯错误。我认为我会坚持使用共享和弱指针作为默认值 - 并且只切换到对象的唯一指针,其中探查器输出表明它值得一试。