I have a class that has a boost::thread
member variable. I have a private member function that is run in that thread (see code below).
class Human
{
public:
Human()
: m_thinkThread(&Human::think, this)
{ }
~Human()
{
m_thinkThread.interrupt();
m_thinkThread.join();
}
private:
void think()
{
// do some thinking...
}
boost::thread m_thinkThread;
};
- Do I need the
interrupt
andjoin
calls and therefore the custom destructor? Or will the default destructor take care of exiting cleanly? If the default destructor is all I need, then what does it do "under the hood" to ensure the thread is exited cleanly? - If I do however, need the
interrupt
andjoin
calls, then my current setup has a bug becausejoin()
canthrow
, which will be an uncaught exception in a destructor. How would I handle this case?
Thank you
CodePudding user response:
Do I need the interrupt and join calls and therefore the custom destructor? Or will the default destructor take care of exiting cleanly? If the default destructor is all I need, then what does it do "under the hood" to ensure the thread is exited cleanly?
Yes. There's std::jthread
in more recent standard versions, which would auto-join.
If I do however, need the interrupt and join calls, then my current setup has a bug because join() can throw, which will be an uncaught exception in a destructor. How would I handle this case?
If I remember correctly there's a join_nothrow
operation as well. In case that's me remembering an implementation detail, consider using a thread guard.
This seems to have been added to Boost Thread after Anthony Williams' "Concurrency In Action" (chapter 2.1.3) which seems to have never received documentation.
struct Human
{
Human() : m_thinkThread(&Human::think, this) {}
private:
void think() const;
boost::thread m_thinkThread;
boost::thread_guard<boost::interrupt_and_join_if_joinable> m_guard{m_thinkThread};
};
void Human::think() const {
while (true) {
boost::this_thread::sleep_for(boost::chrono::milliseconds(1500));
std::cout << "Thinking..." << std::endl;
}
}
int main() {
Human plight;
boost::this_thread::sleep_for(boost::chrono::seconds(10));
}
Prints
Thinking...
Thinking...
Thinking...
Thinking...
Thinking...
Thinking...
then cleanly shuts down after 10 seconds.
Caveats
There have been versions of boost where using thread-guards in a situation with nested interruptable threads there would be unhandled exceptions: see https://github.com/boostorg/thread/issues/366