[英]Catching exception from worker thread in the main thread
我找不到以下問題的簡明答案:我有一個生產者-消費者線程模型,其中主線程是消費者,而某些工作線程是生產者。生產者線程在應用程序執行期間運行它的線程循環主線程是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包裹在主線程中,因為它是在某個時刻創建的,從那時開始它自己運行直到應用程序終止。
首先, 您不需要使用bind
與thread
。 這樣做只會增加不必要的復制,並使代碼更難以閱讀。 我希望每個人都不要這樣做。
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
鎖定多長時間。
另一種方法使用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
並保留,直到通過關聯的Future讀取結果為止。
// 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()
處理任何異常。
這些答案建議您手動將exception_ptr
發送到主線程。 這不是壞方法,但我建議您采用另一種方法: std::promise
/ boost::promise
。
(由於我現在沒有在這台計算機中使用boost,所以我將使用std::promise
。但是,boost可能並沒有太大的區別。)
看一下示例代碼:
#include <iostream>
#include <exception>
#include <thread>
#include <future>
#include <chrono>
void foo()
{
throw "mission failure >o<";
}
int main()
{
std::promise<void> prm;
std::thread thrd([&prm] {
try
{
std::this_thread::sleep_for(std::chrono::seconds(5));
foo();
prm.set_value();
}
catch (...)
{
prm.set_exception(std::current_exception());
}
});
std::future<void> fu = prm.get_future();
for (int i = 0; ; i++)
{
if (fu.wait_for(std::chrono::seconds(1)) != std::future_status::timeout)
break;
std::cout << "waiting ... [" << i << "]\n";
}
try
{
fu.get();
std::cout << "mission complete!\n";
}
catch (const char *msg)
{
std::cerr << "exception: " << msg << "\n";
}
thrd.join(); /* sorry for my compiler's absence of std::promise::set_value_at_thread_exit */
}
這種方式的好處是1.您無需手動管理異常std::promise
和std::future
將完成所有工作,並且2.您可以使用std::future
周圍的所有功能。 在這種情況下,我在等待線程退出時通過std::future::wait_for
做其他事情(輸出waiting...
消息)。
在輔助線程中,您可以捕獲異常,然后使用std::current_exception
檢索std::exception_ptr
。 然后,您可以將其存儲在某個地方,在主線程中進行拾取,然后使用std::rethrow_exception
。
異常是同步的。 這意味着沒有辦法在線程之間將它們作為異常傳遞。 您不能告訴任何舊線程“停止所有操作並處理此問題”。 (好吧,如果您向它傳遞POSIX信號,那不是C ++的例外)。
當然,您總是可以將帶有異常數據的對象(而不是處於異常處理模式的狀態)傳遞給另一個線程,就像在線程之間傳遞任何其他數據一樣。 並發隊列可以。 然后在目標線程中對其進行處理。 目標線程應正在從隊列中主動讀取數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.