2013-03-28 62 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;