我正在研究一组使用boost::asio来执行后台任务的类。实际上,该程序将连续运行,但是我添加了信号处理程序以在测试过程中进行清理。
但是,当在接收到SIGINT之后监视代码中的函数调用时,我发现我的对象的私有(private)实现未如预期那样被破坏-内存泄漏。它通过boost::shared_ptr进行管理。私有(private)实现类如下所示。
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;
};
这是使用私有(private)实现的代码。
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实例持续存在?
最佳答案
循环引用介于TestImpl
和TestImpl::io_svc
之间:
TestImpl::io_svc
的生命周期取决于TestImpl
,因为它是成员变量。 TestImpl
的生命周期间接取决于TestIMpl::io_svc
,这是因为shared_from_this()
被绑定(bind)为在io_service
内排队的处理程序中的实例句柄。 一个关键的细节是,
io_service::stop()
仅影响事件处理循环。它不会影响处理程序的生命周期或绑定(bind)到处理程序的参数。从io_service
中删除处理程序的唯一方法是通过io_service
的destructor。以下是文档中的相关摘录:若要解决此问题,请考虑将ost.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;