当多线程 C++11 程序中的异常未处理时会发生什么?

What happens when an exception goes unhandled in a multithreaded C++11 program?

提问人:R. Martinho Fernandes 提问时间:9/1/2011 最后编辑:sbiR. Martinho Fernandes 更新时间:12/18/2012 访问量:17282

问:

如果我有一个运行两个线程的 C++11 程序,其中一个线程引发未经处理的异常,会发生什么情况?整个程序会死得很惨吗?抛出异常的线程是否会单独死亡(如果是这样,在这种情况下我可以获取异常吗)?完全是别的什么?

C 多线程 异常 ++11 C ++-FAQ

评论

0赞 sje397 9/1/2011
可以肯定的是,线程会死掉,如果不抓住它,你就无法“照顾”它......但我认为其他线程将继续。不过很想听到权威的答案。
1赞 Matthieu M. 9/1/2011
@sje397:我当然不会打赌这个过程会继续下去。我想知道我们是否可以希望异常会跨线程传播,会很好。exception_ptr
0赞 sje397 9/1/2011
看来我错了。好问题。
1赞 Ben Voigt 9/1/2011
@Matthieu:IMO 在跨线程调用期间封送到另一个线程的异常不会被视为“未处理”。

答:

53赞 Ben Voigt 9/1/2011 #1

一切都没有真正改变。n3290 中的措辞是:

如果未找到匹配的处理程序,则调用该函数std::terminate()

可以使用 自定义行为,但是:terminateset_terminate

所需行为:A 应终止程序的执行,而不返回调用方。terminate_handler

因此,在这种情况下程序会退出,其他线程无法继续运行。

评论

1赞 Matthieu M. 9/1/2011
std::exception_ptr允许将异常从一个线程迁移到另一个线程,这种传播是否自动进行,以便在调用线程中抛出异常?(可以说,这可能很困难)
4赞 Branko Dimitrijevic 9/1/2011
@Kerrek 不,您绝对无法捕获其“线程或来源”之外的异常。异常捕获取决于堆栈展开,而堆栈基本上是特定于线程的。
2赞 Martin York 9/1/2011
在 C++03 中(线程超出标准)带有 pthreads。主线程中未捕获的异常会终止应用程序(正常)。但是,子线程中的异常只会终止该线程。令我惊讶的是,这种行为并不是新标准所说的应该实施的。
1赞 Ben Voigt 9/1/2011
@Branko:对于C++0倍期货,例外情况肯定应该与结果一起转移。这意味着必须首先在工作线程中完成展开(因为工作线程甚至可能退出),然后传输异常。
1赞 Ben Voigt 9/1/2011
@Kerrek:阅读第 30.6 节,其中讨论了“期货”并描述了结果(包括异常)是如何传播的。
32赞 Luc Danton 9/2/2011 #2

由于异常传播似乎有合法的兴趣,并且这至少与问题略有关系,因此我的建议是:被视为构建更高级别抽象的不安全原语。它们在异常方面具有双重风险:如果我们刚刚启动的线程内发生异常,一切都会爆炸,正如我们所展示的那样。但是,如果在启动的线程中发生异常,我们可能会遇到麻烦,因为析构函数要求连接或分离(或等效地,不是线程)。违反这些要求会导致......调用 !std::threadstd::threadstd::thread*thisstd::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

当然,有些人可能会争辩说,如果我们简单地编辑我们启动的每个线程,我们在第二点上是安全的。问题在于,在某些情况下,这是最明智的做法。例如,快速排序的“幼稚”并行化需要等到子任务结束。在这些情况下,充当同步原语(集合)。detachjoinjoin

幸运的是,我提到的那些更高层次的抽象确实存在,并且随标准库一起提供。它们是 、 以及 和 。上述的等效异常安全版本:std::asyncstd::futurestd::packaged_taskstd::promisestd::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();

事实上,与其调用调用的线程,不如将责任传递给另一个线程:getasync

// 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

评论

7赞 R. Martinho Fernandes 9/2/2011
+1 绝对同意将其视为不安全的原语。std::thread