#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <stdexcept>
class MyClass {
public:
void run() {
// do some work
}
struct Data {
double x{0.0};
int y{0};
} data;
};
void Thread_function(int i, MyClass myclass, std::exception_ptr &exc, std::mutex &mtx)
{
try {
// Modify some of the struct parameters
auto dataCopy = myclass.data;
dataCopy.x = i + 1;
dataCopy.y = (i + 1) * 0.5;
// run the simulation with changed parameters
myclass.data = dataCopy;
myclass.run();
}
catch (const std::exception &exception)
{
// lock capturedException
std::lock_guard<std::mutex> lock(mtx);
exc = std::current_exception();
}
}
int main() {
const int num_threads = 5;
std::vector<std::thread> threads;
std::exception_ptr capturedException;
std::mutex mtx;
MyClass myClass;
// create threads
for (int i = 0; i < num_threads; ++i)
{
threads.emplace_back(
Thread_function, i, myClass, std::ref(capturedException), std::ref(mtx));
}
// wait for all threads to finish
for (auto& t : threads)
{
t.join();
}
// main thread rethrows exception, if any
if (capturedException)
{
throw capturedException;
return 1;
}
return 0;
}
Several threads are created in the main function which do some computations in the run
function. In the thread function, I used try/catch
block to catch exceptions and std::lock_guard<std::mutex> for synchronization
since exc
is a shared variable among all threads (passed by reference).
In the main function, I wait for all threads to join and then rethrow exception from the threads.
However, in main, I want to return 1
once a thread throwed an exception. No need to wait for the other threads then. At the moment, m̀ain
waits for all threads to finish although one one of the threads possibly throwed an exception already.
How can I fix this? Are there classes in c++ library to handle this smoothly?
Note that I left the previous answer as an example; I've completely rewritten your code, and eliminated your MyClass
since I don't know what it does anyway. In this example, the actual code goes into the MyThread::run
function.'
It also gives a few more outputs.
The important thing is that we no longer wait for exceptions, since threads need to quit anyway. Instead, your run
function needs to check the quit-condition frequently enough to suit your taste -- when one of the threads throws, the other threads will quit next time they call the performQuit
function.
performQuit
works by throwing an exception so it can be called from anywhere inside your stuff. This exception is caught and discarded, though.
A lot of changes are just me generally doing things differently than you.
Note that my example run
runs for "thread number seconds", so they would normally quit every 1 second. This gets aborted when #3 throws its exception after 2.5 seconds.
#include <iostream>
#include <thread>
#include <mutex>
#include <stdexcept>
#include <list>
#include <chrono>
/************************************************************************/
class MyThread
{
private:
static inline const std::chrono::time_point startTime=std::chrono::system_clock::now();
private:
static inline std::mutex mutex;
static inline std::exception_ptr capturedException;
private:
class OperationCancelled { };
static void performQuit()
{
std::lock_guard<decltype(mutex)> lock(mutex);
if (capturedException)
{
throw OperationCancelled();
}
}
static void captureException()
{
// first exception "wins"; any other ones are lost since
// we only have room for one.
std::lock_guard<decltype(mutex)> lock(mutex);
if (!capturedException)
{
capturedException=std::current_exception();
}
}
public:
static void rethrowIf()
{
if (capturedException)
{
std::rethrow_exception(capturedException);
}
}
private:
std::thread thread;
const int number;
private:
void printStatus(std::string_view string)
{
auto elapsed=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()-startTime);
std::lock_guard<decltype(mutex)> lock(mutex);
std::cout << elapsed.count() << "ms: thread " << number << " " << string << "\n";
}
private:
void run()
{
// We need to check the quit condition during the processing, so
// for convenience reasons I'll pretend the actual job has a loop
// so we can just check on each iteration. If it doesn't, add the
// check in between work chunks or whatever -- the thread will
// only quit on a check, so it's up to you how quickly the threads
// will respond to a quit...
for (int i=0; i<100*number; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
performQuit();
if (number==3 && i==250) throw std::runtime_error("boo!");
}
}
private:
// this acts as a wrapper around the "run" function, so we keep
// the clerical tasks separated from the actual function.
void body()
{
printStatus("launched");
try
{
run();
printStatus("has completed task");
}
catch(const OperationCancelled&)
{
printStatus("was aborted");
}
catch(...)
{
printStatus("got exception");
captureException();
}
printStatus("exit");
}
public:
MyThread(int number_)
: number(number_)
{
thread=std::thread([this](){ body(); });
}
~MyThread()
{
thread.join();
}
};
/************************************************************************/
int main() {
const int num_threads = 6;
// create threads and wait for them to finish
{
std::list<MyThread> threads;
for (int i = 0; i < num_threads; ++i)
{
threads.emplace_back(i);
}
}
try
{
MyThread::rethrowIf();
}
catch(const std::exception& exception)
{
std::cerr << "exception: " << exception.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
The output:
stieber@gatekeeper:~ $ g++ Test.cpp && ./a.out
0ms: thread 0 launched
0ms: thread 0 has completed task
0ms: thread 0 exit
0ms: thread 1 launched
0ms: thread 2 launched
0ms: thread 3 launched
0ms: thread 4 launched
0ms: thread 5 launched
1007ms: thread 1 has completed task
1007ms: thread 1 exit
2014ms: thread 2 has completed task
2014ms: thread 2 exit
2528ms: thread 3 got exception
2528ms: thread 3 exit
2538ms: thread 4 was aborted
2538ms: thread 4 exit
2538ms: thread 5 was aborted
2538ms: thread 5 exit
exception: boo!