How can I minimize boilerplate code associated with std::thread?
I have several classes like this in my C++ code:
class ThreadRunner {
public:
void start() {
m_thread = std::thread(&ThreadRunner::runInThread, this);
}
void stop() {
m_terminate = true;
}
~ThreadRunner() {
m_terminate = true;
if (m_thread.joinable()) {
m_thread.join();
}
}
private:
void runInThread() {
size_t i = 0;
while (!m_terminate) {
std::cout << "i: " << i << "\n";
i++;
}
};
std::thread m_thread;
std::atomic<bool> m_terminate{ false };
};
Some of my runInThread
functions take arguments, and some do not.
There is quite a bit of boilerplate code (m_terminate, join) that I have to repeat for each class.
All this is of course motivated by that I do not want terminate to get called inside destructor of std::thread
:
~thread() noexcept {
if (joinable()) {
_STD terminate();
}
}
A colleague has made a class encapsulating this boilerplate.
The way it works is that it has a virtual void runInThread() = 0
method.
So each of my classes would inherit this baseclass and override this method.
Also there is an implicit assumption that each override checks m_terminate
occasionally.
I would prefer to use composition instead of inheritance. That is MyClass uses a MyThread not is a MyThreadObject. How can I create a start method that takes a "function pointer" and arbitrary arguments and passes them on to std::thread constructor?
If it is too much hassle I guess I could will go with my colleague's approach instead. From what I understand Java has a Runnable interface where one must override void Run()
.
If I were to use std::jthread
is this how it should be done?
class ThreadRunner {
public:
void start() {
m_thread = std::jthread(&ThreadRunner::runInThread, this, m_stop_source.get_token());
}
void stop() {
m_stop_source.request_stop();
}
~ThreadRunner() {
stop(); // otherwise ~jthread waits forever for runInThread to finish
}
private:
void runInThread(std::stop_token stoken) {
size_t i = 0;
while (!stoken.stop_requested()) {
std::cout << "i: " << i << "\n";
i++;
}
};
std::jthread m_thread;
std::stop_source m_stop_source;
};
What are the benefits of using a std::stop_source
over a std::atomic<bool> m_terminate
?
If I were to use std::async
is this how it would be done?:
class ThreadRunner {
public:
void start() {
m_future = std::async(std::launch::async, &ThreadRunner::runInThread, this);
}
void stop() {
m_terminate = true;
}
~ThreadRunner() {
stop();
}
private:
void runInThread() {
size_t i = 0;
while (!m_terminate) {
std::cout << "i: " << i << "\n";
i++;
}
};
std::future<void> m_future;
std::atomic<bool> m_terminate{ false };
};
Solution 1:
Your std::jthread
code can be simplified to:
class ThreadRunner {
public:
void start() {
m_thread = std::jthread(&ThreadRunner::runInThread, this);
}
void stop() {
m_thread.request_stop();
}
private:
void runInThread() {
size_t i = 0;
auto stopToken = m_thread.get_stop_token();
while (!stopToken.stop_requested()) {
std::cout << "i: " << i << "\n";
i++;
}
};
std::jthread m_thread;
};
Or as your wrapper isn't doing much just using std::jthread
directly might be simpler:
std::jthread thread([](std::stop_token stoken){
size_t i = 0;
while (!stoken.stop_requested()) {
std::cout << "i: " << i << "\n";
i++;
}
});