【问题标题】:Why not capture the already propagating exception in `std::promise::~promise`为什么不在`std::promise::~promise`中捕获已经传播的异常
【发布时间】:2017-05-15 20:13:08
【问题描述】:

假设你有以下代码

void func(std::promise<int> prom) {
    try {
        auto promise = std::promise<int>{std::move(prom)};
        // .. do stuff
        throw std::runtime_error{"something went wrong"};
    } catch (...) {
        // cleanup and dont bother with the promise
    }
}

auto promise = std::promise<int>{};
auto future = promise.get_future();

std::thread{[promise = std::move(promise)] {
    func(std::move(promise));
}}.detach();

try {
    cout << future.get() << endl;
} catch (std::exception& exc) { 
    cerr << exc.what() << endl;
}

当 Promise 被销毁并在未来结束时重新传播相同的异常时,没有捕获正在传播的异常的原因是什么?为什么在这种情况下总是抛出一个破承诺异常?

我只是觉得在未来重新传播相同的异常是很自然的。


为了澄清我的意思,除了set_exception() 方法之外,我认为调用set_exception(std::current_exception()) 的析构函数可能是个好主意

在捕获异常并存储对它的引用后,析构函数将重新抛出异常

【问题讨论】:

  • 你应该使用set_exception在promise对象中设置异常。
  • @WhiZTiM 我知道这一点,我只是想知道为什么捕获传播异常并调用 set_exception 是个坏主意

标签: c++ c++11 exception c++14 future


【解决方案1】:

析构函数没有异常可以捕获或重新抛出。我认为你想要的逻辑是:

  • 如果析构函数检测到它作为堆栈展开的一部分被调用;和
  • 否则它将以共享状态存储 future_errorbroken_promise,然后
  • 它将current_exception() 存储在共享状态。

让我们忽略这样一个事实,在 C++17 之前,您无法以可移植的方式检测您是否被作为堆栈展开的一部分调用;实现可以访问普通人无法访问的“神奇”事物。

首先,您在线程之间默默地共享异常对象,伴随着数据竞争的所有风险。如果没有标准库在背后捅你一刀,跨线程共享东西就够难了。

其次,生产者方面的例外可能对消费者来说毫无意义。在许多情况下,消费者并不关心生产者是否由于网络错误或月相而未能产生价值。它只在乎承诺被打破了。

【讨论】:

    【解决方案2】:

    您建议std::promise 的析构函数捕获活动异常并存储它们。

    do_stuff 根据你的提议返回什么?

    int do_stuff(std::future& f) {
      std::vector<int> alice;
      std::promise bob;
      f = bob.get_future();
      throw 7;
      alice.push_back(42);
      return alice[0];
    }
    

    do_stuff 返回什么?抛出的7~promise 捕获,但这绕过了返回值计算。

    我在这条路上看到的只有精神错乱。

    异常已经是来自,但至少它们是结构化来自。您的提议需要非结构化捕获,并且会使异常程序流完全超出任何人的理解能力。

    如果它只是复制了异常,一般情况下你不允许这样做吗?您可以存储指向它们的(智能)指针,但不能存储副本。

    【讨论】:

    • 我想我可能误解了我的意思,除了set_exception,我觉得在析构函数中捕获异常可能是个好主意。就像远程异常,thrift 实现的方式,远程端抛出的异常传播到拉端
    • @Curious 是的,这就是我的理解。同样,如果promise 在其析构函数期间神奇地捕获了在其生命周期内抛出的异常,那么do_stuff 会返回什么? promise 捕获异常7do_stuff 的返回值是多少?请指定一个整数,还是应该将抛出的异常传播出去?
    • 重新抛出异常并标记析构函数noexcept(false)?还是我在这里遗漏了什么明显的东西
    • @Curious 为什么它会重新抛出?您的代码不会重新抛出,它会捕获它并等待.get()'d 的未来。上面没有.get()'d 的未来。它应该抓住它并等待它。
    • 是的,这可能是个坏主意。但话又说回来,如果发生某些事情并且异常导致承诺被破坏,是否应该不提醒future
    猜你喜欢
    • 2021-05-13
    • 2011-12-14
    • 2018-01-14
    • 1970-01-01
    • 2019-05-17
    • 2015-03-16
    • 1970-01-01
    • 2016-01-30
    • 2016-09-12
    相关资源
    最近更新 更多