Using C++17, for a worker thread with a non-blocking loop in it that performs some task, I see three ways to signal the thread to exit:
std::atomic_bool
that the thread checks in a loop. If it is set to true
, the thread exits. The main thread sets it to true
before invoking std::thread::join()
.std::condition_variable
with a bool
. This is similar to the above, except it allows you to invoke std::condition_variable::wait_for()
to effectively "sleep" the thread (to lower CPU usage) while it waits for a potential exit signal (via setting the bool
, which is checked in the 3rd argument to wait_for()
(the predicate). The main thread would lock a mutex, change the bool to true
, and invoke std::condition_variable::notify_all()
before invoking std::thread::join()
to signal the thread to exit.std::future
and std::promise
. The main thread holds a std::promise<void>
while the worker thread holds the corresponding std::future<void>
. The worker thread uses std::future::wait_for()
similar to the step above. Main thread invokes std::promise::set_value()
before calling std::thread::join()
.My thoughts on each:
std::this_thread::sleep_for()
. Seems like an "old fashioned" way of doing thread signals.std::future
and std::promise
yet, so I am not sure if it's the ideal solution. In my mind, promise & future are meant to transfer values across threads, not really be used as signals. So I'm not sure if there are efficiency concerns.I see multiple ways of signaling a thread to exit. And sadly, my Google searching has only introduced more as I keep looking, without actually coming to a general consensus on the "modern" and/or "best" way of doing this with C++17.
I would love to see some light shed on this confusion. Is there a conclusive, definitive way of doing this? What is the general consensus? What are the pros/cons of each solution, if there is no "one size fits all"?
If you have a busy working thread which requires one-way notification if it should stop working the best way is to just use an atomic<bool>
. It is up to the worker thread if it wants to slow down or it doesn't want to slow down. The requirement to "throttle" the worker thread is completely orthogonal to the thread cancellation and, in my opinion, should not be considered with the cancellation itself. This approach, to my knowledge, has 2 drawbacks: you can't pass back the result (if any) and you can't pass back an exception (if any). But if you do not need any of those then use atomic<bool>
and don't bother with anything else. It is as modern as any; there is nothing old-fashioned about it.
condition_variable
is part of the consumer/producer pattern. So there is something which produces work and there is something that consumes what was produced. To avoid busy waiting for the consumer while there is nothing to consume the condition_variable
is a great option to use. It is just a perfect primitive for such tasks. But it doesn't make sense for the thread cancellation process. And you will have to use another variable anyway because you can't rely on condition_variable
alone. It might spuriously wake up the thread. You might "set" it before it gets in the waiting process, losing the "set" completely, and so on. It just can't be used alone so we back to square one but now with an atomic<bool>
variable to accompany our condition_variable
The future
/promise
pair is good when you need to know the result of the operation done on the other thread. So it is not a replacement of the approach with the atomic<bool>
but it rather complements it. So to remove the drawbacks described in the first paragraph you add future
/promise
to the equation. You provide the calling side with the future
extracted from the promise
which lives within the thread. That promise
gets set once the thread is finished:
atomic<bool>
variable. So as you see the future
/promise
pair just helps to provide some feedback for the callee it has nothing to do with the cancellation itself.
P.S. You can always use an electric sledgehammer to crack a nut but it doesn't make the approach any more modern.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With