gpt4 book ai didi

c++ - 如何正确关闭使用 asio 进行事件排队的类实例

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

在我的项目中,我广泛使用 boost asio,以便能够使用 io_service.post() 和 strand.post()/dispatch() 将非统一事件统一排队到我的应用程序中的模块。

在 main() 中,这些模块被创建并保存在 shared_ptr 中,直到程序退出并被删除:

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);
}

在析构函数中:

manager_a::~manager_a()
{
m_module->unregister(this);
}

管理器通过链发布调用来实现回调接口(interface):

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
}

现在我所处的困境:

如果“模块”在 register(this) 之后立即调用回调在构造函数中,main中的shared_ptr还没有接管实例,shared_from_this()在回调函数中抛出异常。与析构函数相同的问题 - shared_ptr 已确定它具有最后一个引用,并且在调用析构函数时,如果回调在 unregister(this) 之前被调用, shared_from_this() throw 。

为什么shared_from_this()的原因之所以需要,是因为排队的函数调用存储在 io_service 中,它包含指向管理器实例的指针,并且独立于管理器的生命周期。我可以给一个原始的 this到“模块”,因为我可以在 manager_a 被销毁之前注销它,但对于 io_service 中排队的函数调用,情况并非如此,因此 shared_from_this()只要发布了一些消息,就需要使实例保持事件状态。你也不能取消它们,也不能在析构函数中阻塞等待,直到所有待处理的帖子都被传递。不幸的是,就像现在一样,我什至无法阻止新帖子在关键的构造函数/析构函数阶段排队(试图)。

一些想法:

  1. 编写启动/停止函数并在那里注册/注销。例如。像这样创建一个工厂函数:

    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 提供一个在删除前调用 stop() 的自定义删除器无济于事,因为无论如何都为时已晚。

  2. 让 main() 调用 start() 和 stop(),但我看不出 main() 如何以异常安全的方式为 stop() 调用执行此操作,即确保调用 stop() 即使后面的代码抛出。让另一个 RAII 类来调用 stop() 似乎很尴尬。

  3. 只需将 strand.dispatch() 调用包装在 try/catch 中并忽略异常。这表明正在进行破坏(或构造尚未完成),因此请忽略回调。这感觉很糟糕,我宁愿不那样做。如果我可以访问 enable_shared_from_this 中的嵌入式 weak_ptr基类,我可以调用非抛出 lock()并检查返回值是否有效。但是 shared_from_this 不给你访问权限,而且它仍然看起来很老套......此外,在构建过程中缺少回调也没有帮助,即使我可以解决这个问题。

  4. 让 manager_a 拥有自己的 io_service 和驱动它的线程。然后,在析构函数中,我可以停止服务并加入线程,并确保 io_service 中没有待处理的帖子。也不需要 shared_from_this()不再,或为一条线。我想这会起作用,但是,在 main() 中为所有管理器设置一个线程池变得毫无意义,而且我的应用程序中的线程比看起来合理的多得多。在不使用 asio io_service 的其他模块中已经有足够的线程...

最佳答案

从你的问题中不清楚,但如果你所有的管理器对象都是在启动时创建并在退出时删除,那么你的问题的解决方案是在启动 IO 线程之前创建管理器,并停止 IO 线程在删除经理之前。这将阻止您在不需要时收到回调。

关于c++ - 如何正确关闭使用 asio 进行事件排队的类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9574587/

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