在我的項目中,我廣泛使用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()
即可發佈一些消息。你不能取消這些,並且你不能阻止 - 在析構函數中等待,直到所有待處理的帖子被傳遞。不幸的是,現在,我甚至無法阻止新的帖子在關鍵的構造/析構階段被排隊(試圖排隊)。
一些想法:
寫入開始/停止功能和不註冊/註銷那裏。例如。創建一個像這樣的工廠功能:
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()似乎很尷尬。
只需在try/catch中包裝strand.dispatch()調用並忽略該異常。這表示正在銷燬(或未完成建設),所以請忽略回撥。這感覺很駭人,我寧願不這樣做。如果我有權訪問
enable_shared_from_this
基類中的嵌入式weak_ptr,我可以調用非拋出lock()
並檢查返回值以查看它是否有效。但是shared_from_this不會給你提供訪問權限,而且它仍然看起來很亂。另外,在施工期間錯過回調沒有幫助,即使我可以解決這個問題。讓manager_a擁有它自己的io_service和一個驅動它的線程。然後,在析構函數中,我可以停止服務並加入線程,並確保io_service中沒有未處理的帖子。也不再需要
shared_from_this()
,或者對於一個鏈。我想,這會起作用,但是,然後,在main()中爲所有管理器創建一個線程池變得毫無意義,而且我的應用程序中會有更多的線程比看起來更明智。有足夠的線程已經在不使用ASIO io_service對象等模塊...
您可以在'module'中使用互斥鎖來阻止任何對它的調用,直到註冊完成。除此之外,選項1對我來說看起來很好。使用它有什麼問題?爲什麼不使用自定義刪除工作? – 2012-03-07 13:33:11
你的意思是,'register(this)'調用應該是原子的嗎?它已經是,這不是問題。問題是,一旦註冊完成,'module'可以自由回叫,並且它可以。自定義刪除器不起作用,因爲智能指針在調用析構函數或自定義刪除器之前釋放引用*。 – 2012-03-12 21:21:07