What happens when an exception is handled in a multithreaded C ++ 11 program?

If I have a C ++ 11 program running on two threads and one of them throws an unhandled exception, what happens? Will the whole program die a fiery death? Will the thread in which the exception is thrown die alone (and if so, can I get the exception in this case)? Is something else completely?

+55
c ++ multithreading c ++ - faq exception c ++ 11
Sep 01 '11 at 14:50
source share
2 answers

Nothing changed. Wording in n3290:

If no matching handler is found, the std::terminate() function is called

The terminate behavior can be customized using set_terminate , but:

Mandatory behavior: A terminate_handler terminates program execution without returning to the caller.

Thus, the program exits in this case, other threads cannot continue to work.

+46
01 Sep 2018-11-11T00:
source share

Since there seems to be a legitimate interest in throwing exceptions, and this is a little, at least somewhat related to the question, here's my suggestion: std::thread should be considered an unsafe primitive for building, for example. higher level abstractions. They are doubly risky for exceptions: if an exception comes out of the thread that we just launched, everything, as we have shown, everything explodes. But if the exception disappears in the thread that started std::thread , we may have problems because std::thread destructor requires *this be either connected or disconnected (or, what is the same, be a non-thread ) Violation of these requirements leads to ... a call to std::terminate !

Hazard code card 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 

Of course, some may argue that if we just detach we edit every thread that we start, we will be safe at this second point. The problem is that in some situations, join is the most sensible thing. For example, the β€œnaive” parallelization of quicksort requires you to wait until the subtasks end. In such situations, join serves as a synchronization primitive (rendez-vous).

Fortunately for us, those higher-level abstractions that I talked about exist and come with a standard library. They are std::async , std::future , as well as std::packaged_task , std::promise and std::exception_ptr . Equivalent, exception-free version above:

 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(); 

And in fact, instead of calling get on a thread called async , you can instead pass the dollar to another thread:

 // 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 
+25
Sep 01 '11 at 17:15
source share



All Articles