【问题标题】:boost::asio acceptor avoid memory leakboost::asio 接受器避免内存泄漏
【发布时间】:2012-09-22 14:56:28
【问题描述】:

使用 boost::asio 我使用 async_accept 来接受连接。这很好用,但是有一个问题,我需要一个如何处理它的建议。使用典型的 async_accept:

  Listener::Listener(int port)
        : acceptor(io, ip::tcp::endpoint(ip::tcp::v4(), port))
        , socket(io) {
          start_accept();
  }

  void Listener::start_accept() {
      Request *r = new Request(io);
      acceptor.async_accept(r->socket(), 
        boost::bind(&Listener::handle_accept, this, r, placeholders::error));
  }

工作正常,但有一个问题:Request 对象是用普通的 new 创建的,因此它可以内存“泄漏”。不是真正的泄漏,它仅在程序停止时泄漏,但我想让 valgrind 开心。

当然有一个选项:我可以用 shared_ptr 替换它,并将它传递给每个事件处理程序。这将一直有效,直到程序停止,当 asio io_service 停止时,所有对象都将被销毁并且 Request 将被释放。但是这样我总是必须有一个 Request 的活动 asio 事件,否则它将被破坏!我认为它是直接崩溃的方式,所以我也不喜欢这种变体。

UPD 第三个变体:Listener 保存 shared_ptr 到活动连接的列表。看起来很棒,除非找到更好的方法,否则我更喜欢使用它。缺点是:由于此模式允许在空闲连接上进行“垃圾收集”,因此它不安全:从侦听器中删除连接指针将立即销毁它,当某些连接的处理程序在其他线程中处于活动状态时可能导致段错误。在这种情况下,使用互斥锁不能解决这个问题,我们必须锁定几乎任何东西。

有没有办法让 acceptor 以美观安全的方式与连接管理一起工作?我很高兴听到任何建议。

【问题讨论】:

  • 这不是您的Listener 类的设计问题,还是您使用带有指针而不是函数对象的bind 的策略,而不是@ 的问题987654325@上课?
  • 它不是函数对象=) 但无论如何我看不出普通函数如何解决这个问题..
  • @PSIAlt 您能否详细说明为什么惯用的shared_ptr/enable_shared_from_this 方法不起作用?我不了解 活动 asio 事件的上下文。此外,如果Request 没有创建自己的异步调用链,并将其生命周期绑定到链,那么其他对象是否维护Request 对象的句柄?
  • @twsansbury 作为“活动的 asio 事件”,我的意思是一些挂起的 async_read/async_write 或任何持有 shared_ptr 以保持连接对象活动的东西。关于Request 对象存储——它很好的变体,但也有缺点;我把它添加到问题中

标签: c++ networking boost boost-asio


【解决方案1】:

使用此库时避免内存泄漏的典型方法是使用shared_ptrio_service documentation 特别提到了这一点

备注

上述的销毁顺序允许程序简化 他们的资源管理使用shared_ptr<>。一个物体在哪里 生命周期与连接的生命周期(或其他一些 异步操作序列),对象的shared_ptr 将 绑定到所有关联的异步操作的处理程序中 用它。其工作原理如下:

当单个连接结束时,所有关联的异步操作 完全的。相应的处理程序对象被销毁,并且所有 shared_ptr 对对象的引用被破坏。关闭 整个程序,调用io_service函数stop()终止 任何run() 尽快致电。 io_service 定义的析构函数 以上销毁所有处理程序,导致所有shared_ptr 引用所有 要销毁的连接对象。

对于您的方案,将您的 Listener::handle_accept() 方法更改为采用 boost::shared_ptr<Request> 参数。您的第二个顾虑

从监听器中移除连接指针会立即销毁它, 当某些连接的处理程序处于活动状态时,什么会导致段错误 在其他线程中。在这种情况下,使用互斥锁不能解决这个问题,我们必须 几乎可以锁定任何东西。

通过从您的类中的 boost::enable_shared_from_this 模板继承得到缓解:

class Listener : public boost::enable_shared_from_this<Listener>
{
   ...
};

那么当你调度处理程序时,在绑定到Listener 的成员函数时使用shared_from_this() 而不是this

【讨论】:

  • 正如我所提到的,在这种情况下,如果没有活动的异步事件,连接就无法存在,这对我不利,因为我必须处理其他异步连接(获取数据)来处理每个请求,所以时间当我获取数据时,使用连接是空闲的,而不是它上面的事件
  • @PSIAlt 我已经更新了我的答案,你想使用shared_from_this()
  • 感谢帮助,我试试这个,看起来很合理
【解决方案2】:

如果有人感兴趣,我找到了另一种方法。 Listener 保存 shared_ptr 到活动连接的列表。连接结束/终止是通过 io_service::post 进行的,它调用 Listener::FinishConnection 并用 asio::strand 包裹。通常我总是用 strand 包装 Request 的方法——它在 DDOS 和/或线程安全方面更安全。因此,使用 strandpost 调用 FinishConnection 可以防止其他线程中的段错误

【讨论】:

    【解决方案3】:

    不确定这是否与您的问题直接相关,但通过使用 Boost Asio 库,我也遇到了类似的内存泄漏,特别是您提到的 acceptor 对象。原来我没有正确关闭服务;一些连接将保持打开状态,并且它们相应的对象不会从内存中释放。调用以下代码消除了 Valgrind 报告的泄漏:

    acceptor.close();
    

    希望这对某人有用!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-08
      • 2013-06-24
      • 1970-01-01
      相关资源
      最近更新 更多