std::async 应该尊重抛出的错误吗?
Should std::async respect thrown errors?
我试图了解如何异步处理异常 -- 我有一个网络服务器,其中包含一个用于处理请求的 lambda 处理程序 (uWebsockets),但它一直在崩溃。为了模拟我使用 std::async
的场景
void call(function<void()> fn) {
std::async([&fn]{
fn();
});
}
int main() {
try {
call([](){
throw std::runtime_error("Oops");
});
} catch (std::runtime_error &e) {
cout<<"Caught the error"<<endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100000));
}
好像是运行std::async里面的函数导致运行时报错一直没执行...程序就当什么都没发生一样退出了...为什么会这样?
让我们一步一步来。让我们从 very definition of what std::async
does 开始。它:
runs the function f asynchronously (potentially in a separate thread
这里“潜在”这个词有点让人分心,但重点是不同的执行线程 .出于所有实际原因,得到 std::async
-ed 的函数可以,并且在现代 C++ 实现中,它 将 运行在新的执行线程中。
首先,具有不同执行线程的真正原因是它们完全独立于它们的父执行线程.不同的执行线程必须正确地实现 排序 操作,以便它们之间以某种形式或方式相互交换信息。
try {
} catch (std::runtime_error &e) {
cout<<"Caught the error"<<endl;
}
异常的概念,以及 try
和 catch
与它们的关系,在您的普通 C++ 教科书中通过解释 try
/catch
将如何捕获来介绍在 try
/catch
块 内发生的异常。一旦执行离开 try
/catch
块,异常处理就不再有效,并且不会捕获任何异常(除非执行在另一个 try
/catch
块内) .
结合执行线程的概念和异常处理的典型解释,您可能会或可能不会立即清楚,您必须得出一个必然的结论,即您只能捕获抛出的异常来自同一个执行线程.
此外,在显示的代码中,绝对没有任何东西可以 保证 原始执行线程仍然在 try
/catch
当另一个执行线程中的异常被抛出时阻塞。事实上,当异常被抛出时,父执行线程很可能已经离开了原始的 try
/catch
块并且它正在 sleep_for
-ing 。离开 try
/catch
块后,您将无法再捕获异常。
但即使原始执行线程仍在try
/catch
块内,这也不会有任何区别,因为抛出的异常只能由同一个执行线程捕获,但是std::async
执行的函数将 运行 在完全不同的执行线程中执行。
编辑:
std::async([&fn]{
这通过引用捕获 fn
。 fn
是一个函数参数,它超出范围并在函数 returns 时被销毁,但不能保证新的执行线程会在它之前引用它。对于异常处理机制(以及 std::async
本身如何处理抛出的异常)而言,这无关紧要,但这仍然需要解决。
你在主线程中没有观察到异常的原因是async
returns 的future
的值从来没有被查询过。如果您像这样扩展 call
的实现:
void call(function<void()> fn) {
std::async([&fn]{
fn();
}).get(); // .get() added here
}
然后观察到异常,因为函数等待结果。
即使没有这个修改,call
也有明确定义的行为,尽管有 @SamVarshavchik 的分析,因为 future
async
returns 在特定方面是特殊的: its destructor waits for the function to complete(请参阅此处的注释部分)。也就是说,call
不会 return 直到被调用的线程完成(抛出异常)。
我试图了解如何异步处理异常 -- 我有一个网络服务器,其中包含一个用于处理请求的 lambda 处理程序 (uWebsockets),但它一直在崩溃。为了模拟我使用 std::async
的场景void call(function<void()> fn) {
std::async([&fn]{
fn();
});
}
int main() {
try {
call([](){
throw std::runtime_error("Oops");
});
} catch (std::runtime_error &e) {
cout<<"Caught the error"<<endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100000));
}
好像是运行std::async里面的函数导致运行时报错一直没执行...程序就当什么都没发生一样退出了...为什么会这样?
让我们一步一步来。让我们从 very definition of what std::async
does 开始。它:
runs the function f asynchronously (potentially in a separate thread
这里“潜在”这个词有点让人分心,但重点是不同的执行线程 .出于所有实际原因,得到 std::async
-ed 的函数可以,并且在现代 C++ 实现中,它 将 运行在新的执行线程中。
首先,具有不同执行线程的真正原因是它们完全独立于它们的父执行线程.不同的执行线程必须正确地实现 排序 操作,以便它们之间以某种形式或方式相互交换信息。
try {
} catch (std::runtime_error &e) {
cout<<"Caught the error"<<endl;
}
异常的概念,以及 try
和 catch
与它们的关系,在您的普通 C++ 教科书中通过解释 try
/catch
将如何捕获来介绍在 try
/catch
块 内发生的异常。一旦执行离开 try
/catch
块,异常处理就不再有效,并且不会捕获任何异常(除非执行在另一个 try
/catch
块内) .
结合执行线程的概念和异常处理的典型解释,您可能会或可能不会立即清楚,您必须得出一个必然的结论,即您只能捕获抛出的异常来自同一个执行线程.
此外,在显示的代码中,绝对没有任何东西可以 保证 原始执行线程仍然在 try
/catch
当另一个执行线程中的异常被抛出时阻塞。事实上,当异常被抛出时,父执行线程很可能已经离开了原始的 try
/catch
块并且它正在 sleep_for
-ing 。离开 try
/catch
块后,您将无法再捕获异常。
但即使原始执行线程仍在try
/catch
块内,这也不会有任何区别,因为抛出的异常只能由同一个执行线程捕获,但是std::async
执行的函数将 运行 在完全不同的执行线程中执行。
编辑:
std::async([&fn]{
这通过引用捕获 fn
。 fn
是一个函数参数,它超出范围并在函数 returns 时被销毁,但不能保证新的执行线程会在它之前引用它。对于异常处理机制(以及 std::async
本身如何处理抛出的异常)而言,这无关紧要,但这仍然需要解决。
你在主线程中没有观察到异常的原因是async
returns 的future
的值从来没有被查询过。如果您像这样扩展 call
的实现:
void call(function<void()> fn) {
std::async([&fn]{
fn();
}).get(); // .get() added here
}
然后观察到异常,因为函数等待结果。
即使没有这个修改,call
也有明确定义的行为,尽管有 @SamVarshavchik 的分析,因为 future
async
returns 在特定方面是特殊的: its destructor waits for the function to complete(请参阅此处的注释部分)。也就是说,call
不会 return 直到被调用的线程完成(抛出异常)。