如何处理并行std算法的异常



我正在使用std::for_each执行n个任务,这些任务可以取消。因此,为了做到这一点,我有一个标志,如果任务被取消,它将被设置为true,从而在任务的代码中引发一些异常。如果我使用正常的std::for_each,它会很好地工作,但如果我使用任何stdexecution_policy,它就会中止。有没有办法阻止我的代码中止?

#include <execution>
#include <array>
#include <chrono>
#include <exception>
using namespace std::chrono_literals;
auto main(int argc, char* argv[]) -> int {
std::array<int, 5> x {1, 2, 3, 4, 5};
std::atomic<bool> toBeCancelled = true;
std::for_each(std::execution::par, std::begin(x), std::end(x), [&](const int& x) {
std::this_thread::sleep_for(2s);
if (toBeCancelled)
throw std::runtime_error("taskCancelled");
});
return 0;
}

任务数量可以是1000-10000。我通常不使用异常,而是在";如果条件";。但这里的任务数量太大了;如果条件";不值得。

正如您所看到的,在std::terminate的痛苦下,您不能让异常从传递给for_each的可调用对象中传出。但你不需要例外,你知道你正在取消任务。

[&](const int& x) {
std::this_thread::sleep_for(2s);
if (toBeCancelled)
return;
}

旁白:在C++20中,我们得到了std::stop_token,它是用于这种信令的。

来自cppreference执行策略:

在使用这些执行策略执行并行算法期间,如果元素访问函数的调用通过未捕获的异常退出,则会调用std::terminate,但实现可能会定义其他执行策略,以不同的方式处理异常。

因此,如果它抛出,您的程序将终止。

如何处理并行std算法的异常

然后cppreference std::thread就有了你的问题的答案:

[…]函数可以通过std::promise或通过修改共享变量(可能需要同步,请参阅std::mutex和std::atomic(将其返回值或异常传递给调用者。

使用共享变量并记住锁定。cppreference execution policies中甚至有一个例子可以增加一个整数,稍微调整一下你的代码可能会像这样:

std::mutex m;
std::vector<std::exception> throwed;
std::for_each(std::execution::par_unseq, std::begin(a), std::end(a), [&](int) {
std::this_thread::sleep_for(2s);
{
std::lock_guard<std::mutex> guard(m);
if (toBeCancelled) {
throwed.push_back(std::runtime_error("taskCancelled"));
}
}
});

或者您可以捕获异常并push_back它。

最新更新