由于对异常传播似乎有合理的兴趣,并且这与问题至少有些相关,所以我的建议是:get被认为是构建不安全的原语,例如 更高层次的抽象。 它们在异常情况下具有很高的风险:如果异常在我们刚刚启动的线程内发生,则一切都会炸毁,如我们所展示的。 但是,如果在启动async的线程中发生异常,我们可能会遇到麻烦,因为std::packaged_task的析构函数要求将std::promise联接或分离(或等效地,不是线程)。 违反这些要求将导致...致电std::exception_ptr!
get的危险代码图:
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
当然,有些人可能会争辩说,如果我们仅对启动的每个线程进行get的处理,那第二点就很安全。 问题是在某些情况下async是最明智的选择。 例如,快速排序的“天真”并行化需要等待子任务结束。 在这些情况下,std::packaged_task用作同步原语(集合点)。
对我们来说幸运的是,我提到的那些更高层次的抽象确实存在并且随标准库一起提供。 它们是get、async以及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
// 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外,您还可以将buck传递给另一个线程:
// only one call to get allowed per std::future so
// this replaces the previous call to get
auto handle = [](std::future 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