Подтвердить что ты не робот

Что происходит, когда исключение обрабатывается в многопоточной программе С++ 11?

Если у меня есть программа С++ 11, работающая на двух потоках, и одна из них бросает необработанное исключение, что происходит? Будет ли вся программа умирать огненной смертью? Будет ли поток, в котором исключение выбрасывается, умирает в одиночку (и если да, могу ли я получить исключение в этом случае)? Что-то еще полностью?

4b9b3361

Ответ 1

Ничего не изменилось. Формулировка в n3290:

Если соответствующий обработчик не найден, функция std::terminate() называется

Поведение terminate можно настроить с помощью set_terminate, но:

Обязательное поведение: A terminate_handler завершает выполнение программы, не возвращаясь к вызывающему.

Таким образом, программа выходит в таком случае, другие потоки не могут продолжать работать.

Ответ 2

Так как, похоже, существует законный интерес к распространению исключений, и это немного, по крайней мере, несколько имеет отношение к вопросу, здесь мое предложение: std::thread следует считать небезопасным примитивом для построения, например. абстракции более высокого уровня. Они вдвойне рискованны по исключениям: если исключение выходит из потока, который мы только что запустили, все, как мы показали, все взрывается. Но если исключение исчезнет в потоке, который запустил std::thread, у нас могут быть проблемы, потому что std::thread destructor требует, чтобы *this был либо соединен, либо отсоединен (или, что то же самое, быть не-нитью). Нарушение этих требований приводит к... вызову std::terminate!

Кодовая карта опасностей std::thread:

auto run = []
{
    // if an exception escapes here std::terminate is called
};
std::thread thread(run);

// notice that we do not detach the thread
// if an exception escapes here std::terminate is called

thread.join();
// end of scope

Конечно, некоторые могут утверждать, что если мы просто detach отредактируем каждый поток, который мы запускаем, мы будем в безопасности на этой второй точке. Проблема в том, что в некоторых ситуациях join - самая разумная вещь. Например, "наивная" распараллеливание quicksort требует подождать, пока подзадачи не закончатся. В таких ситуациях join служит в качестве примитива синхронизации (rendez-vous).

К счастью для нас, те абстракции более высокого уровня, о которых я говорил, существуют и поставляются со стандартной библиотекой. Они std::async, std::future, а также std::packaged_task, std::promise и std::exception_ptr. Эквивалентная, исключающая исключение версия выше:

auto run = []() -> T // T may be void as above
{
    // may throw
    return /* some T */;
};

auto launched = std::async(run);
// launched has type std::future<T>

// may throw here; nothing bad happens

// expression has type T and may throw
// will throw whatever was originally thrown in run
launched.get();

И фактически вместо вызова get в потоке, который называется async, вы можете вместо этого передать доллар в другой поток:

// only one call to get allowed per std::future<T> so
// this replaces the previous call to get
auto handle = [](std::future<T> future)
{
    // get either the value returned by run
    // or the exception it threw
    future.get();
};

// std::future is move-only
std::async(handle, std::move(launched));
// we didn't name and use the return of std::async
// because we don't have to