Что происходит, когда исключение не обрабатывается в многопоточной программе C++11?
Если у меня есть программа C++11, работающая с двумя потоками, и один из них выдает необработанное исключение, что происходит? Неужели вся программа умрет огненной смертью? Будет ли поток, в котором выбрасывается исключение, умирать в одиночку (и если да, то могу ли я получить исключение в этом случае)? Что-то совсем другое?
2 ответа:
ничего не изменилось. Формулировка в n3290-это:
если соответствующий обработчик не найден, то функция
std::terminate()называетсяповедение
terminateможно настроить с помощьюset_terminate, но:требуемое поведение: A
terminate_handlerпрекращает выполнение программы, не возвращаясь к вызывающему абоненту.таким образом, программа выходит в таком случае, другие потоки не могут продолжаться бегущий.
поскольку, похоже, существует законный интерес к распространению исключений, и это немного, по крайней мере, несколько относится к вопросу, вот мое предложение:
std::threadсчитается небезопасным примитивом для построения, например, абстракций более высокого уровня. Они же вдвойне рискованное исключение: если исключение происходит внутри потока, который мы только что запустили, все взрывается, как мы показали. Но если исключение происходит в потоке, который запустилstd::threadмы можем потенциально быть в беде, потому чтоstd::threadдеструктор требует этого*thisбыть соединенным или отделенным (или, что то же самое, быть not-a-thread). Результатом является нарушение этих требований... звонок в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конечно, некоторые могут возразить, что если мы просто
detached каждый поток, который мы запускаем, мы в безопасности на этой второй точке. Проблема в том, что в некоторых ситуацияхjoin- самый разумная вещь, чтобы сделать. Например, "наивное" распараллеливание quicksort требует подождать, пока подзадачи не закончатся. В таких ситуацияхjoinслужит примитивом синхронизации (рандеву).к счастью для нас, эти абстракции более высокого уровня, о которых я упоминал, существуют и поставляются со стандартной библиотекой. Они
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