C++ 如何正确关闭使用asio进行事件队列的类的实例
在我的项目中,我广泛使用boost asio,以便能够使用io_service.post()和strand.post()/dispatch()将非统一事件统一排队到应用程序中的模块 在main()中,这些模块被创建并保存在共享_ptr中,直到程序退出并被删除:C++ 如何正确关闭使用asio进行事件队列的类的实例,c++,multithreading,boost,boost-asio,shared-ptr,C++,Multithreading,Boost,Boost Asio,Shared Ptr,在我的项目中,我广泛使用boost asio,以便能够使用io_service.post()和strand.post()/dispatch()将非统一事件统一排队到应用程序中的模块 在main()中,这些模块被创建并保存在共享_ptr中,直到程序退出并被删除: simplified main: { [some initialization] boost::asio::io_service service; [create pool of worker threads that d
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)]
}
在析构函数中:
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”在构造函数中的寄存器(this)
之后立即调用回调,则main中的shared\u ptr尚未接管实例,回调函数中的shared\u from\u this()
抛出异常。析构函数也存在同样的问题-共享的\u ptr已确定它具有最后一个引用,并且当调用析构函数时,如果在取消注册(this)
之前调用回调,则从\u this()抛出共享的\u
之所以需要shared\u from\u this()
,是因为排队函数调用存储在io\u服务中,该服务持有指向管理器实例的指针,并且独立于管理器的生存期。我可以给“模块”一个原始的this
,因为我可以在manager\u a被销毁之前注销它,但是对于io\u服务中的排队函数调用,情况并非如此,因此需要一个shared\u from\u 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;
}
boost::共享ptr管理器\u a::创建(io\u服务与s,模块*m)
{
boost::shared_ptr p(新经理a(s,m));
p->start();
返回p;
}
这只适用于创造,而不适用于毁灭。为共享的\u ptr提供一个自定义的deleter(在delete之前调用stop())没有帮助,因为无论如何已经太晚了
让main()调用start()和stop(),但我不知道main()如何以异常安全的方式为stop()调用执行此操作,也就是说,即使稍后有代码抛出,也要确保调用stop()。让另一个RAII类仅仅调用stop()似乎很尴尬
只需将strand.dispatch()调用包装为try/catch并忽略异常。这表示正在进行销毁(或构建尚未完成),因此忽略回调。这让人觉得有点刻薄,我不想那样做。如果我可以从这个
基类访问enable\u shared\u中嵌入的弱ptr,我可以调用非抛出的lock()
并检查返回值是否有效。但是,从共享的__这并没有给你访问权限,而且它仍然看起来很黑客。。。此外,在构造过程中错过回调也没有帮助,即使我可以解决这个问题
让manager_a拥有自己的io_服务和驱动它的线程。然后,在析构函数中,我可以停止服务并加入线程,确保io_服务中没有挂起的帖子。也不再需要从此()共享的shared\u,也不需要串。我想这是可行的,但这样一来,在main()中为所有管理器提供一个线程池就变得毫无意义了,而且我的应用程序中的线程会比看起来合理的多得多。其他模块中已经有足够多的线程不使用asio\U服务
您的问题并不清楚,但如果所有管理器对象都是在启动时创建的,并在退出时删除,那么问题的解决方案是在启动IO线程之前创建管理器,并在删除管理器之前停止IO线程。这将阻止您在不需要回拨时收到回拨。您可以使用模块中的互斥来停止对它的任何调用,直到注册完成。除此之外,选项1在我看来很好。你使用它有什么问题?为什么使用定制的删除器不起作用?你的意思是,寄存器(this)
调用应该是原子的?已经是了,这不是问题所在。问题是,一旦注册完成,模块
就可以自由地回拨,而且它确实可以。自定义删除程序不起作用,因为智能指针会在调用析构函数或自定义删除程序之前释放引用。好主意。不幸的是,如果管理者需要该服务进行清理,例如向其子工作人员(连接实例)发布终止请求,那么这也不会起作用。。。无论如何,我会接受这个答案,因为考虑到后果,它应该是有效的。到目前为止,我的问题中的想法是4。谢谢
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;
}