【问题标题】:Catching exception from boost::asio::io_service::work running as a detached thread从作为分离线程运行的 boost::asio::io_service::work 捕获异常
【发布时间】: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,是当前设计的结果。

标签: c++ multithreading boost boost-asio


【解决方案1】:

您可以在工作线程中捕获异常,将它们保存到两个线程共享的队列变量中,并在主线程中定期检查该队列。

要使用队列,您需要先将异常转换为通用类型。您可以使用std::exceptionstring 或任何最适合您情况的方法。如果您绝对需要保留原始异常类的信息,可以使用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(&amp;boost::asio::io_service::run, &amp;ioService));以上...
  • @Mendes 虽然您可能无法控制io_service::run(),但您确实可以控制线程的入口点。考虑创建一个在 try/catch 块中调用 io_service.run() 的函数,根据需要传播异常,并将此函数指定为线程入口点。
  • @Mendes 我改变了我的答案,它现在应该适用于你的情况。
猜你喜欢
  • 2017-09-17
  • 1970-01-01
  • 2016-01-07
  • 1970-01-01
  • 2011-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
相关资源
最近更新 更多