从作为分离线程运行的 boost::asio::io_service::work 捕获异常
Posted
技术标签:
【中文标题】从作为分离线程运行的 boost::asio::io_service::work 捕获异常【英文标题】:Catching exception from boost::asio::io_service::work running as a detached thread 【发布时间】:2016-03-05 12:41:42 【问题描述】:我有我的应用程序主循环控制,我在其中启动一个线程来处理 asio 工作,如下所示:
void AsioThread::Run()
try
/*
* Start the working thread to io_service.run()
*/
boost::asio::io_service::work work(ioservice);
boost::thread t(boost::bind(&boost::asio::io_service::run, &ioService));
t.detach();
while (true)
// Process stuff related to protocol, calling
// connect_async, send_async and receive_async
catch (std::runtime_error &ex)
std::cout << "ERROR IN FTP PROTOCOL: " << ex.what() << std::endl;
catch (...)
std::cout << "UNKNOWN EXCEPTION." << std::endl;
在异步操作期间,处理程序被调用,有时我会在这些处理程序上抛出异常,例如:
void AsioThread::ReceiveDataHandler(const boost::system::error_code& errorCode, std::size_t bytesTransferred)
std::cout << "Receive data handler called. " << bytesTransferred << " bytes received." << std::endl;
if (errorCode)
std::cout << "Error receiving data from server." << std::endl;
rxBufferSize = bytesTransferred;
/*
* Check for response
*/
std::string msg(rxBuffer);
if (msg.substr(0, 3) != "220")
throw std::runtime_error("Error connecting to FTP Server");
我的问题是异步处理程序 (AsioThread::ReceiveDataHandler
) 中抛出的异常没有被AsioThread::Run
中的主处理循环try...catch
块捕获。自然会发生这种情况,因为工作线程 t
在另一个线程上,处于分离状态,并且在运行时会导致执行错误。
如何从分离的boost::asio::io_service::work
线程接收异常?如何构建我的代码以使这个逻辑起作用?
感谢您的帮助。
【问题讨论】:
为什么会有一个单独的while (true)
事件循环?你可以只使用 asio 事件循环来做所有事情吗?在这种情况下加入线程。如果它可以用完工作,请创建一个 io_service::work 对象。
我需要事件循环,因为它作为协议逻辑的状态处理器......没有它就无法实现。
不确定我是否理解你,但通常协议状态是在异步回调中处理的。不需要为此设置单独的事件循环。例如,在 async_connect 之后的回调中,您将被连接等。事件处理程序启动下一个异步操作,例如async_connect 处理程序也可以开始读取和写入。
是的,这就是正在做的事情,但是整个逻辑运行从客户端类调用,该类向 maon 循环发送和接收命令,如 Connect、SendFile 等......这就是为什么我确实有一个接收这些消息的主循环......就像嵌入协议的命令控制概念......
为什么异常需要传播到AsioThread::Run()
?是否只退出while
循环?您能否提供一个关于while(true)
循环在做什么的代码示例?这个问题感觉像是XY problem,是当前设计的结果。
【参考方案1】:
您可以在工作线程中捕获异常,将它们保存到两个线程共享的队列变量中,并在主线程中定期检查该队列。
要使用队列,您需要先将异常转换为通用类型。您可以使用std::exception
或string
或任何最适合您情况的方法。如果您绝对需要保留原始异常类的信息,可以使用boost::exception_ptr。
您需要的变量(这些可能是AsioThread
的成员):
boost::mutex queueMutex;
std::queue<exceptionType> exceptionQueue;
在工作线程中运行这个函数:
void AsioThread::RunIoService()
try
ioService.run();
catch(const exceptionType& e)
boost::lock_guard<boost::mutex> queueMutex;
exceptionQueue.push(e);
catch(...)
boost::lock_guard<boost::mutex> queueMutex;
exceptionQueue.push(exceptionType("unknown exception"));
像这样启动工作线程:
boost::thread t(boost::bind(&AsioThread::RunIoService, this));
t.detach();
在主线程中:
while(true)
// Do something
// Handle exceptions from the worker thread
bool hasException = false;
exceptionType newestException;
boost::lock_guard<boost::mutex> queueMutex;
if(!exceptionQueue.empty())
hasException = true;
newestException = exceptionQueue.front();
exceptionQueue.pop();
if(hasException)
// Do something with the exception
This blog post 实现了一个线程安全的队列,你可以使用它来简化保存异常;在这种情况下,您将不需要单独的互斥锁,因为它位于队列类中。
【讨论】:
这是个好主意,但我没有编写工作线程...它由boost::asio
处理。见boost::thread t(boost::bind(&boost::asio::io_service::run, &ioService));
以上...
@Mendes 虽然您可能无法控制io_service::run()
,但您确实可以控制线程的入口点。考虑创建一个在 try/catch 块中调用 io_service.run()
的函数,根据需要传播异常,并将此函数指定为线程入口点。
@Mendes 我改变了我的答案,它现在应该适用于你的情况。以上是关于从作为分离线程运行的 boost::asio::io_service::work 捕获异常的主要内容,如果未能解决你的问题,请参考以下文章