Что происходит, когда исключение не обрабатывается в многопоточной программе 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
конечно, некоторые могут возразить, что если мы просто
detach
ed каждый поток, который мы запускаем, мы в безопасности на этой второй точке. Проблема в том, что в некоторых ситуациях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