我找不到以下问题的简明答案:我有一个生产者-消费者线程模型,其中主线程是消费者,而某些工作线程是生产者。生产者线程在应用程序执行期间运行它的线程循环主线程是UI线程,应该弹出异常消息,包括来自不同线程的异常消息。如何在主线程中捕获这些异常?

在Windows上使用C++ 0x boost

WorkerThread.cpp

WorkerThread::WorkerThread(){

   m_thread = boost::thread(&WorkerThread::drawThread,this);

}

void WorkerThread::drawThread()
{

         while(true)
         {
             boost::unique_lock<boost::mutex> lock(m_mutex);
              try{

                ///some work is done here...

              }catch(std::exception &e){

               /// some exception is thrown
               /// notify main thread of the exception
              }

         }


 }

需要注意的重要一点是,我无法使用try {} catch将WorkerThread包裹在主线程中,因为它是在某个时刻创建的,从那时开始它自己运行直到应用程序终止。

最佳答案

首先,不需要将bindthread 一起使用。这样做只会增加不必要的复制,并使代码更难以阅读。我希望每个人都不要这样做。

WorkerThread::WorkerThread(){

    m_thread = boost::thread(&WorkerThread::drawThread, this);

}

您可以将异常存储在exception_ptr中,并将其传递给其他线程,例如在std::queue<std::exception_ptr>中:
void WorkerThread::drawThread()
{
    while(true)
    {
        boost::unique_lock<boost::mutex> lock(m_mutex);
         try{

            ///some work is done here...

         }catch(std::exception &e){
             m_queue.push(std::current_exception());
         }
    }
}

std::exception_ptr WorkerThread::last_exception()
{
    boost::lock_guard<boost::mutex> lock(m_mutex);
    std::exception_ptr e;
    if (!m_queue.empty())
    {
        e = m_queue.front();
        m_queue.pop();
    }
    return e;
}

然后在另一个线程中重新抛出它并处理它:
if (auto ep = workerThread.last_exception())
{
    // do something with exception
    try
    {
        std::rethrow_exception(ep);
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error in worker thread: " << e.what() << '\n';
    }
}

如果您不能使用std::exception_ptr,Boost会有自己的实现,但是我不确定current_exception的Boost等同于什么。您可能需要将异常包装在另一个对象中,以便Boost异常传播机制可以存储该异常。

您可能希望对主队列中的异常队列使用单独的互斥锁(并在m_mutex块内移动try锁),具体取决于工作线程通常将m_mutex锁定了多长时间。

另一种方法是使用C++ 11 Future,它可以更方便地处理线程之间的异常传递。您需要某种方式使主线程获得工作线程运行的每个工作单元的 future ,这可以通过std::packaged_task完成:
class WorkerThread
{
public:
  WorkerThread();   // start m_thread, as before

  template<typename F, typename... Args>
  std::future<void> post(F f, Args&&... args)
  {
    Task task(std::bind<void>(f, std::forward<Args>(args)...));
    auto fut = task.get_future();
    std::lock_guard<std::mutex> lock(m_mutex);
    m_tasks.push(std::move(task));
    return fut;
  }

  private:
    void drawThread();
    std::mutex m_mutex;
    using Task = std::packaged_task<void()>;
    std::queue<Task> m_tasks;
    std::thread m_thread;
  };

 void WorkerThread::drawThread()
 {
    Task task;
    while(true)
    {
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            task = std::move(m_tasks.front());
            m_tasks.pop();
        }
        task();   // run the task
    }
}

运行任务时,将捕获任何异常,将其存储在exception_ptr中并保留,直到在相关的将来读取结果为止。
// other thread:

auto fut = workerThread.post(&someDrawingFunc, arg1, arg2);
...
// check future for errors
try {
   fut.get();
} catch (const std::exception& e) {
   // handle it
}

当将工作发布给使用者时,生产者线程可以将future对象存储在队列中,而其他一些代码段可以检查队列中的每个将来以查看是否就绪,然后调用get()来处理任何异常。

09-10 04:40
查看更多