2013-03-28 118 views
3

我正在使用boost :: asio來執行後臺任務的一組類。實際上,該程序會持續運行,但我在測試期間添加了信號處理程序進行清理。在使用boost :: asio和boost :: thread時瞭解內存泄漏

但是,當收到SIGINT後監視代碼中的函數調用時,我發現我的對象的私有實現沒有像預期的那樣被銷燬 - 內存泄漏。它使用boost :: shared_ptr進行管理。私有實現類如下所示。

class TestImpl: public boost::enable_shared_from_this<TestImpl>, boost::noncopyable { 
    TestImpl(): update_timer(io_svc), signals(io_svc, SIGINT, SIGTERM) { 
     signals.async_wait(boost::bind(&boost::asio::io_service::stop, &io_svc)); 
    }; 

public: 
    virtual ~TestImpl() { 
     std::cout << "Destroyed." << std::endl; 
    }; 

    static boost::shared_ptr<TestImpl> create() { 
     boost::shared_ptr<TestImpl> ptr(new TestImpl); 
     ptr->start(); 
     return ptr; 
    } 

    void start() { 
     update_timer.expires_from_now(boost::posix_time::seconds(1)); 
     update_timer.async_wait(boost::bind(&TestImpl::update, shared_from_this()));  

     run_thread = boost::thread(boost::bind(&TestImpl::run, shared_from_this())); 
    }; 

    void cleanup() { 
     run_thread.join(); 
    }; 

private: 
    void run() { 
     io_svc.run(); 
    }; 

    void update() { 
     std::cout << "Updating." << std::endl; 
     update_timer.expires_from_now(boost::posix_time::seconds(1)); 
     update_timer.async_wait(boost::bind(&TestImpl::update, shared_from_this()));  
    }; 

    boost::asio::io_service io_svc; 
    boost::asio::deadline_timer update_timer; 
    boost::thread run_thread; 
    boost::asio::signal_set signals; 
}; 

下面是使用私有實現的代碼。

class Test { 
public: 
    Test(): impl(TestImpl::create()) { }; 
    virtual ~Test() { std::cout << "Destroyed." << std::endl; }; 
    int run() { 
     boost::asio::signal_set signals(io_svc, SIGINT, SIGTERM); 
     signals.async_wait(boost::bind(&boost::asio::io_service::stop, &io_svc)); 

     io_svc.run(); 

     impl->cleanup(); 

     return 0; 
    }; 
private: 
    boost::asio::io_service io_svc; 
    boost::shared_ptr<TestImpl> impl; 
}; 

int main() { 
    Test test; 
    test.run(); 
} 

我無法理解爲什麼TestImpl類被泄漏。通過調試,我可以驗證兩個io_service實例在SIGINT上停止並且該線程被加入,這導致我相信在銷燬時它不會被分離。似乎必須有一個引起TestImpl實例持續存在的循環引用?

回答

4

圓形參考是TestImplTestImpl::io_svc之間:壽命

  • TestImpl::io_svc的取決於TestImpl,因爲它是一個成員變量。
  • TestImpl的壽命間接取決於TestIMpl::io_svc由於shared_from_this()束縛作爲實例處理程序中處理io_service內排隊。

關鍵的細節是io_service::stop()隻影響事件處理循環;它不影響處理程序的生命週期或與處理程序相關的論據。從io_service中刪除處理程序的唯一方法是通過io_servicedestructor。下面是從文檔的相關摘錄:

未調用處理程序對象被安排延期調用的io_service是,或任何相關股,被銷燬。

[...]

要關閉整個程序中,io_service功能stop()被調用,以儘快終止任何run()電話。上面定義的析構函數破壞所有處理程序,導致所有連接對象的所有shared_ptr引用都被銷燬。

要解決此問題,請考慮解耦Boost.Asio I/O對象的使用壽命從TestImpl。我個人會選擇使用boost::optional而不是boost::shared_ptr來減少內存分配的數量。

TestImpl() 
    : io_svc(boost::in_place()), 
    update_timer(boost::in_place(boost::ref(io_svc.get()))), 
    signals(boost::in_place(boost::ref(io_svc.get()), SIGINT, SIGTERM)) 
{ 
    signals->async_wait(boost::bind(&boost::asio::io_service::stop, 
            boost::ref(io_svc))); 
}; 

... 

void cleanup() { 
    run_thread.join(); 
    signals  = boost::none; 
    update_timer = boost::none; 
    io_svc  = boost::none; 
}; 

... 

boost::optional<boost::asio::io_service> io_svc; 
boost::optional<boost::asio::deadline_timer> update_timer; 
boost::optional<boost::asio::signal_set> signals; 
boost::thread run_thread;