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;
  }

异常的概念,以及 trycatch 与它们的关系,在您的普通 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]{

这通过引用捕获 fnfn 是一个函数参数,它超出范围并在函数 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 直到被调用的线程完成(抛出异常)。