2012-10-01 104 views
4

使用boost :: asio我使用async_accept来接受连接。这很好,但有一个问题,我需要一个建议如何处理它。利用典型async_accept:boost :: asio acceptor避免内存泄漏

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)); 
    } 

工作正常,但有一个问题:请求对象与普通因此它可以内存“泄漏”创建。不是真的泄漏,它只在程序停止时泄漏,但是我想让012g幸福。

确定有一个选项:我可以将其替换为shared_ptr,并将其传递给每个事件处理程序。这将工作,直到程序停止,当asio io_service正在停止,所有对象将被销毁,请求将被free'd。但这样我总是必须有一个活跃的asio事件请求,否则它将被销毁!我认为它的直接方式崩溃,所以我不喜欢这个变种。

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

有没有办法使acceptor工作与连接管理一些美丽和安全的方式?我会很高兴听到任何建议。

+1

这不是你的'Listener'类的设计问题,或者你的策略是用指针而不是函数对象来使用'bind',而不是'acceptor'类的问题? – Hurkyl

+0

它不是一个函数对象=)但无论如何,我没有看到如何纯函数可以解决这个问题。 – PSIAlt

+0

@PSIAlt请你详细说明为什么习惯'shared_ptr' /'enable_shared_from_this'方法不起作用?我不明白* active * asio事件的上下文。另外,如果'Request'没有创建它自己的异步调用链,并且将它的生命周期绑定到链上,那么其他对象是否维护'Request'对象的句柄? –

回答

1

的典型配方使用这个库是使用shared_ptr时避免内存泄漏,所述io_servicedocumentation特别提到这

备注

上述破坏序列允许程序简化 它们的资源管理通过使用shared_ptr<>。如果对象的生命周期与连接的生命周期(或其他一些异步操作的序列)相关联,则对象的shared_pt将被绑定到与其相关联的所有异步操作的处理程序中 。这工作如下:

当单个连接结束时,所有关联的异步操作 完成。相应的处理程序对象被销毁,所有对对象的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

+0

正如我所提到的,在这种情况下,如果没有活动的异步事件,连接就不能存在,对我来说有什么坏处我必须处理其他异步连接(获取数据)来处理每个请求,所以当我获取数据使用连接是空闲的,而不是事件 – PSIAlt

+0

@PSIAlt我已经更新了我的答案,您想使用'shared_from_this()'。 –

+0

感谢您的帮助,生病尝试这个,看起来很合理 – PSIAlt

0

如果有人感兴趣,我找到了另一种方式。 Listener将shared_ptr列表保存到活动连接。终止/终止连接通过io_service::post进行,其中包裹asio::strand。通常情况下,我总是将Request的方法包装在一起 - 在DDOS和/或线程安全性方面更安全。因此,使用strand调用从postFinishConnection从段错误在其他线程保护

0

不知道这是否是直接关系到你的问题,但我也使用升压短耳库具有类似内存泄漏,尤其是相同的acceptor您提到的对象。原来我没有正确关闭服务,一些连接将保持打开状态,并且相应的对象不会从内存中释放。调用以下摆脱了由Valgrind的报道的泄漏:

acceptor.close(); 

希望这可以成为有用的人!