gpt4 book ai didi

c++ - 了解使用 boost::asio 和 boost::thread 时的内存泄漏

转载 作者:太空狗 更新时间:2023-10-29 21:02:21 27 4
gpt4 key购买 nike

我正在研究一组使用 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 实例持续存在?

最佳答案

循环引用在TestImplTestImpl::io_svc之间:

  • TestImpl::io_svc 的生命周期取决于 TestImpl,因为它是一个成员变量。
  • TestImpl 的生命周期间接取决于 TestIMpl::io_svc,因为 shared_from_this() 被绑定(bind)为处理程序中排队的实例句柄io_service

一个关键的细节是 io_service::stop()只影响事件处理循环;它不会影响处理程序或绑定(bind)到处理程序的参数的生命周期。从 io_service 中删除处理程序的唯一方法是通过 io_servicedestructor .以下是文档的相关摘录:

Uninvoked handler objects that were scheduled for deferred invocation on the io_service, or any associated strand, are destroyed.

[...]

To shut down the whole program, the io_service function stop() is called to terminate any run() calls as soon as possible. The io_service destructor defined above destroys all handlers, causing all shared_ptr references to all connection objects to be destroyed.

要解决此问题,请考虑将 Boost.Asio I/O 对象的生命周期与 TestImpl 分离。我个人会选择使用 boost::optionalboost::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;

关于c++ - 了解使用 boost::asio 和 boost::thread 时的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15671954/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com