Ответ
Самый удобный способ — использовать std::future, которая автоматически захватывает исключения, брошенные в асинхронной задаче. При вызове get() исключение пробрасывается в вызывающий поток.
Пример с std::async и сбором исключений:
#include <future>
#include <vector>
#include <iostream>
#include <stdexcept>
int risky_operation(int id) {
if (id % 3 == 0) {
throw std::runtime_error("Bad luck from task " + std::to_string(id));
}
return id * 100;
}
int main() {
std::vector<std::future<int>> futures;
// Запускаем несколько асинхронных задач
for (int i = 0; i < 10; ++i) {
futures.emplace_back(std::async(std::launch::async, risky_operation, i));
}
std::vector<std::exception_ptr> exceptions;
std::vector<int> results;
// Собираем результаты и исключения
for (auto& fut : futures) {
try {
results.push_back(fut.get()); // get() пробросит исключение, если оно было
} catch (...) {
exceptions.push_back(std::current_exception()); // Сохраняем исключение для последующей обработки
}
}
// Обрабатываем все собранные исключения
for (const auto& eptr : exceptions) {
try {
if (eptr) std::rethrow_exception(eptr);
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << 'n';
}
}
return 0;
}
Альтернатива для ручного управления потоками: Можно хранить std::exception_ptr в разделяемой переменной, защищенной мьютексом, и каждый поток в блоке catch(...) должен сохранять туда std::current_exception().