C++ is more reliant on its call stack than C is. C++ programs often use RAII, which means that resources are bound to objects that frequently live on the stack. Users of such objects expect those objects to be destroyed properly. If a function creates a stack object, it expects that at some point in the future, control will return to that function and the stack object will be destroyed.
As such, there is no mechanism for a thread with some stack depth to simply go away. A std::thread
only ends when the end of the function passed to the thread
constructor is reached (an exception emitted from a thread function invokes std::terminate
).
Given this, it is best to restructure your code so that you never need to cause a thread function to exit from some arbitrary place in the call graph. Make it so that the only points where you would want the current thread to exit are places within the thread's main function.
For example, the typical way a thread pool works is that each thread's main function goes to sleep, waiting on some form of task to be dropped off for that thread. When a task is available, it executes that task. Upon the task's completion, it checks for a new task, and if none is available, it goes back to sleep until a task is ready.
In such a thread pool, no thread ever stops. Individual tasks stop, but the actual std::thread
is eternal (or at least, lives as long as the pool).
If a task needs to terminate, then such termination essentially represents a failure to perform the task. In C++, that's spelled "throwing an exception". The main thread would put all task invocations in a try
block, with a catch
block for the specific exception type. It can then report to whomever that a task failed, then go check for a new task.
And this makes sure that the task's call stack is cleaned up.