在多线程 C++11 程序中未处理异常时会发生啥?
Posted
技术标签:
【中文标题】在多线程 C++11 程序中未处理异常时会发生啥?【英文标题】:What happens when an exception goes unhandled in a multithreaded C++11 program?在多线程 C++11 程序中未处理异常时会发生什么? 【发布时间】:2011-11-08 11:34:42 【问题描述】:如果我有一个运行两个线程的 C++11 程序,其中一个抛出未处理的异常,会发生什么?整个节目会不会死得一塌糊涂?抛出异常的线程会单独死掉吗(如果是这样,在这种情况下我可以获得异常吗)?完全不同的东西?
【问题讨论】:
很确定线程会死掉,如果不将其捕获在线程中,您将无法“处理”它......但我认为其他线程将继续。不过很想听到权威的答案。 @sje397:我当然不会打赌这个过程会继续存在。我想知道如果使用exception_ptr
,我们是否可以希望异常能够跨线程传播,会很好。
看来我错了。好问题。
@Matthieu:在跨线程调用期间编组到另一个线程的异常不会被视为“未处理”,IMO。
【参考方案1】:
什么都没有真正改变。 n3290 中的写法是:
如果没有找到匹配的处理程序,则调用函数
std::terminate()
terminate
的行为可以用set_terminate
自定义,但是:
必需的行为:
terminate_handler
应终止程序的执行而不返回调用者。
所以这种情况下程序退出,其他线程无法继续运行。
【讨论】:
std::exception_ptr
允许将异常从一个线程迁移到另一个线程,这种传播是否自动化,以便在调用线程中引发异常? (可以说,这可能很困难)
@Kerrek 不,您绝对不能在其“线程或来源”之外捕获异常。异常捕获取决于堆栈展开,而堆栈基本上是特定于线程的。
在 C++03 中(线程是否超出标准)与 pthreads。未捕获的主线程中的异常会杀死应用程序(正常)。但是子线程中的异常只会杀死该线程。我很惊讶这种行为不是新标准所说的应该实施的。
@Branko:对于 C++0x 期货,异常肯定应该与结果一起传输。这意味着必须先在工作线程中展开展开(因为工作线程甚至可能退出),然后才转移异常。
@Kerrek:阅读第 30.6 节,其中讨论了“期货”并描述了结果(包括异常)是如何传播的。【参考方案2】:
由于似乎对异常传播有正当的兴趣,并且这与问题略有相关,因此我的建议是:std::thread
被认为是构建不安全的原语,例如更高层次的抽象。它们在异常方面是双重风险的:如果异常在我们刚刚启动的线程内发生,那么一切都会爆炸,正如我们所展示的那样。但是如果在启动std::thread
的线程中出现异常,我们可能会遇到麻烦,因为std::thread
的析构函数要求*this
要么加入要么分离(或者等效地,not-a-线程)。违反这些要求会导致...调用std::terminate
!
std::thread
的危险代码图:
auto run = []
// if an exception escapes here std::terminate is called
;
std::thread thread(run);
// notice that we do not detach the thread
// if an exception escapes here std::terminate is called
thread.join();
// end of scope
当然,有些人可能会争辩说,如果我们只是 detach
ed 我们启动的每个线程,我们在第二点上是安全的。问题在于,在某些情况下join
是最明智的做法。例如,快速排序的“幼稚”并行化需要等到子任务结束。在这些情况下,join
用作同步原语(集合点)。
幸运的是,我提到的那些更高级别的抽象确实存在并且随标准库一起提供。它们是std::async
、std::future
以及std::packaged_task
、std::promise
和std::exception_ptr
。上述等效的异常安全版本:
auto run = []() -> T // T may be void as above
// may throw
return /* some T */;
;
auto launched = std::async(run);
// launched has type std::future<T>
// may throw here; nothing bad happens
// expression has type T and may throw
// will throw whatever was originally thrown in run
launched.get();
实际上,您可以将责任推给另一个线程,而不是在调用 async
的线程中调用 get
:
// only one call to get allowed per std::future<T> so
// this replaces the previous call to get
auto handle = [](std::future<T> future)
// get either the value returned by run
// or the exception it threw
future.get();
;
// std::future is move-only
std::async(handle, std::move(launched));
// we didn't name and use the return of std::async
// because we don't have to
【讨论】:
+1 绝对同意将std::thread
视为不安全的原语。以上是关于在多线程 C++11 程序中未处理异常时会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章