从主线程中的工作线程捕获异常 [英] Catching exception from worker thread in the main thread

查看:100
本文介绍了从主线程中的工作线程捕获异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

在Windows上使用C ++ 0x增强功能

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异常传播机制可以存储该异常.

您可能希望使用与主工作循环不同的互斥锁作为异常队列(并在try块内移动m_mutex锁),这取决于工作线程通常将m_mutex锁定了多长时间. /p>


一种不同的方法使用C ++ 11 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()来处理任何异常

I didn't find a concise answer to the following problem:I have a producer - consumer threading model where main thread is the consumer while some worker thread is the producer.The producer thread runs it's thread loop during the application execution and it is possible for it to throw exceptions occasionally.The main thread is UI thread which should pop-up exception messages including those coming from different threads. How can I catch these exceptions in the main thread?

Using boost on Windows with C++0x

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
              }

         }


 }

Important to note that I have no ability to wrap WorkerThread in the main thread with try{}catch as it is created at some point and from then on runs on its own till the application termination.

解决方案

Firstly, you do not need to use bind with thread. Doing so just adds unnecessary copying and makes the code harder to read. I wish everyone would stop doing that.

WorkerThread::WorkerThread(){

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

}

You can store an exception in an exception_ptr and pass that to the other thread, e.g. in 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;
}

Then in the other thread rethrow it and handle it:

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

If you can't use std::exception_ptr Boost has its own implementation of it, but I'm not sure what the Boost equivalent of current_exception is. You might need to wrap the exception in another object so the Boost exception propagation mechanism can store it.

You might want to use a separate mutex for the exception queue from the main work loop (and move the m_mutex lock inside the try block) depending how long m_mutex is usually locked by the worker thread.


A different approach uses C++11 futures, which handle passing exceptions between threads more conveniently. You need some way for the main thread to get a future for each unit of work the worker thread runs, which can be done with 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
    }
}

When the task is run any exceptions will be caught, stored in an exception_ptr and held until the result is read through the associated future.

// other thread:

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

The producer thread could store the future objects in a queue when posting work to the consumer, and some other piece of code could check each future in the queue to see if it's ready and call get() to handle any exception.

这篇关于从主线程中的工作线程捕获异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆