2012-03-05 67 views
3

在我的項目中,我廣泛使用boost asio來將非統一事件統一排列到應用程序中的模塊,使用io_service.post()和strand.post()/調度()。如何正確關閉使用asio進行事件排隊的類的實例

在main()中創建和shared_ptrs一直保持到程序退出這些模塊,它們被刪除:

simplified main: 
{ 
    [some initialization] 

    boost::asio::io_service service; 

    [create pool of worker threads that drive the io_service] 

    { 
    boost::shared_ptr<manager_a> a(new manager_a(service, foo)); 
    boost::shared_ptr<manager_b> b(new manager_b(service, bar, blah)); 
    ... 

    [wait for signal to shutdown (SIGINT (release), cin.getc() (debug), or similar)] 
    } 

    [some shutdown (join thread pool)] 
} 

的「經理人」(我不知道該怎麼稱呼他們)派生來自boost :: enable_shared_from_this。 在它們的構造函數,它們可以與其他模塊註冊回調:

manager_a::manager_a(boost::asio::io_service& service, module *m) : 
    m_service(service), 
    m_strand(service), 
    m_module(m) 
{ 
    // manager_a implements some callback interface 
    // that "module" may call from another thread 
    m_module->register(this); 
} 

在析構函數:

void manager_a::on_module_cb(module_message m) 
{ 
    // unsafe to do work here, because the callback is from an alien thread 
    m_strand.dispatch(boost::bind(&manager_a::handle_module_message, shared_from_this(), m)); 
} 
void manager_a::handle_module_message(module_message m) 
{ 
    // safe to do work here, as we're serialized by the strand 
} 

manager_a::~manager_a() 
{ 
    m_module->unregister(this); 
} 

經理通過發佈通過鏈的調用實現回調接口

我現在處於的困境:

我f「模塊」在構造函數中的register(this)之後立即調用回調函數,主函數中的shared_ptr尚未接管實例,回調函數中的shared_from_this()將引發異常。與析構函數相同的問題 - shared_ptr已確定它具有最後一個引用,並且調用析構函數時,如果在拋出unregister(this)shared_from_this()之前調用回調函數。

爲什麼需要shared_from_this()是因爲排隊函數調用存儲在io_service中,該調用包含一個指向管理實例的指針,並且獨立於經理的生存期。我可以給原始的this添加到「模塊」,因爲我可以在管理器_a銷燬之前取消註冊它,但是對於io_service中的排隊函數調用也是如此,因此只要有一個shared_from_this()即可發佈一些消息。你不能取消這些,並且你不能阻止 - 在析構函數中等待,直到所有待處理的帖子被傳遞。不幸的是,現在,我甚至無法阻止新的帖子在關鍵的構造/析構階段被排隊(試圖排隊)。

一些想法:

  1. 寫入開始/停止功能和不註冊/註銷那裏。例如。創建一個像這樣的工廠功能:

    boost::shared_ptr<manager_a> manager_a::create(io_service& s, module *m) 
    { 
        boost::shared_ptr<manager_a> p(new manager_a(s, m)); 
        p->start(); 
        return p; 
    } 
    

    這隻適用於創建,而不是破壞。給自定義刪除程序的shared_ptr,在delete之前調用stop()並沒有幫助,因爲它無論如何已經太晚了。 ()調用start()和stop(),但我沒有看到main()如何以stop()調用的異常安全的方式執行此操作,即確保調用停止()即使稍後的代碼拋出。有另一個RAII類只是叫stop()似乎很尷尬。

  2. 只需在try/catch中包裝strand.dispatch()調用並忽略該異常。這表示正在銷燬(或未完成建設),所以請忽略回撥。這感覺很駭人,我寧願不這樣做。如果我有權訪問enable_shared_from_this基類中的嵌入式weak_ptr,我可以調用非拋出lock()並檢查返回值以查看它是否有效。但是shared_from_this不會給你提供訪問權限,而且它仍然看起來很亂。另外,在施工期間錯過回調沒有幫助,即使我可以解決這個問題。

  3. 讓manager_a擁有它自己的io_service和一個驅動它的線程。然後,在析構函數中,我可以停止服務並加入線程,並確保io_service中沒有未處理的帖子。也不再需要shared_from_this(),或者對於一個鏈。我想,這會起作用,但是,然後,在main()中爲所有管理器創建一個線程池變得毫無意義,而且我的應用程序中會有更多的線程比看起來更明智。有足夠的線程已經在不使用ASIO io_service對象等模塊...

+0

您可以在'module'中使用互斥鎖來阻止任何對它的調用,直到註冊完成。除此之外,選項1對我來說看起來很好。使用它有什麼問題?爲什麼不使用自定義刪除工作? – 2012-03-07 13:33:11

+0

你的意思是,'register(this)'調用應該是原子的嗎?它已經是,這不是問題。問題是,一旦註冊完成,'module'可以自由回叫,並且它可以。自定義刪除器不起作用,因爲智能指針在調用析構函數或自定義刪除器之前釋放引用*。 – 2012-03-12 21:21:07

回答

1

這不是從你的問題不清楚,但如果在啓動時創建,並在退出刪除了所有經理的對象,那麼解決問題的辦法是在啓動IO線程之前創建管理器,並在刪除管理器之前停止IO線程。這會阻止你在不需要它們時回調。

+0

好主意。不幸的是,如果管理者需要清理服務,例如將終止請求發送給他們的子工(連接實例),那麼這也不會起作用......無論如何,我會接受這個答案,因爲考慮到後果時它應該起作用。到目前爲止,我正在從我的問題中提出想法#4。謝謝! – 2012-04-07 14:56:41