Как можно проверить несколько потоков на наличие исключений в C++?

«Как можно проверить несколько потоков на наличие исключений в C++?» — вопрос из категории Многопоточность, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Самый удобный способ — использовать 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().