2012-10-01 103 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(); 

希望這可以成爲有用的人!