首页 文章

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

提问于
浏览
3

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

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

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

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

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
}

现在我陷入困境:

如果"module"在构造函数中的 register(this) 之后立即调用回调,则main中的shared_ptr尚未接管该实例,并且回调函数中的 shared_from_this() 会引发异常 . 与析构函数相同的问题 - shared_ptr确定它具有最后一个引用,并且在调用析构函数时,如果在 unregister(this) 之前调用回调,则抛出 shared_from_this() .

需要 shared_from_this() 的原因是因为排队的函数调用存储在io_service中,该调用包含指向管理器实例的指针,并且独立于管理器的生命周期 . 我可以给"module"原始的 this 因为我可以在manager_a被销毁之前取消注册,但是对于io_service中的排队函数调用,情况也是如此,所以只要有's some message posted. You can' t就需要 shared_from_this() 来保持实例存活取消这些,你甚至可以阻止新的帖子在关键的构造函数/析构函数阶段排队(试图)排队 .

一些想法:

  • 写入启动/停止功能并在那里注册/取消注册 . 例如 . 创建一个像这样的工厂函数:
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(),没有用,因为无论如何都太晚了 .

  • 让main()调用start()和stop(),但是我没有看到main()如何以异常安全的方式执行stop()调用,即确保调用stop()甚至如果某些后来的代码抛出 . 让另一个RAII类调用stop()似乎很尴尬 .

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

  • 让manager_a拥有它's own io_service and a thread driving it. Then, in the destructor, I can stop the service and join the thread and be sure there are no pending posts in the io_service. There'也不再需要 shared_from_this() ,或者一条链 . 我想这会有用,但是然后,在main()中为所有管理器创建一个线程池变得毫无意义,而且我使用asio io_service ...

1 回答

  • 1

    从您的问题中不清楚,但是如果所有管理器对象都是在启动时创建的并且在退出时删除,则问题的解决方案是在启动IO线程之前创建管理器,并在删除之前停止IO线程经理们 . 当你不想要它们时,这将阻止你获得回调 .

相关问题