Reputation: 2329
I would like to use a std::condition_variable as an exit condition for a method called on a thread. I envision that it will be used like this:
std::mutex m;
std::condition_variable exit_condition;
void func() {
std::unique_lock<std::mutex> lk(m);
while (exit_condition.wait_for(lk, std::chrono::milliseconds(0)) == std::cv_status::timeout)
// Do something
}
int main() {
std::thread t(func);
// Do something
exit_condition.notify_one();
t.join();
}
However it seems that with a zero timeout, the return value of wait_for
is always std::cv_status::timeout
.
If I were to write func
using the Windows API, I would write it like this:
HANDLE exit_condition;
void func() {
while (WaitForSingleObject(exit_condition, 0) == WAIT_TIMEOUT)
// Do something
}
Note that I can write func
with a small timeout and it will exit.
void func() {
std::unique_lock<std::mutex> lk(m);
while (exit_condition.wait_for(lk, std::chrono::milliseconds(10)) == std::cv_status::timeout)
// Do something
}
I'm a bit concerned that under some conditions it will not exit though.
So, can I check a std::condition_variable without a timeout in some way that will guarantee the return value of the wait_for
method will be std::cv_status::no_timeout
?
Upvotes: 1
Views: 3514
Reputation: 2329
To summarize the answers and the various discussion topics, I wanted to pull things together in one answer.
The answer to my original question is yes, you can check a std::condition_variable without a timeout, but it must be one of the wait
overloads which accepts a predicate.
First, it seems that a std:condition_variable
may not be the best tool to signal a thread to exit . A std::mutex
and a shared bool
or an std::atomic<bool>
or std::atomic_flag
work better.
If you do plan to use std::condition_variable
as I proposed in the question, be aware of the possibility of a spurious wake. According to 3.50.1[#10] in the C++11 standard, a call to wait
, wait_for
, or wait_until
may return without a call to notify_one
, notify_all
, or the predicate returning true.
This implies two guidelines:
wait
returns must explicitly check the return value of the predicate.wait
methods which do not accept a predicate should be called.The following code accomplishes the goal of the original code in my answer, but does so safely.
std::mutex m;
std::condition_variable exit_condition;
bool should_exit;
void func() {
std::unique_lock<std::mutex> lk(m);
while (!exit_condition.wait_for(lk, std::chrono::milliseconds(0), []{return should_exit;})) {
if (should_exit)
break;
// Do something
}
}
int main() {
should_exit = false;
std::thread t(func);
// Do something
should_exit = true;
exit_condition.notify_one();
t.join();
}
Upvotes: 0
Reputation: 16898
Condition variables are not event objects. In particular, they don't have memory.
Set an explicit exit flag. And you actually do polling, so there's no need for any condition variables (merits of polling discussion aside).
bool exit_request = false;
std::mutex m;
void func() {
while (true) {
{
std::unique_lock<std::mutex> lk(m);
if (exit_request)
return;
}
do_something();
}
}
int main() {
std::thread t(func);
// Do something
{
std::unique_lock<std::mutex> lk(m);
exit_request = true;
}
t.join();
}
PS. And, yeah, as said above, a simple atomic flags will do in this case.
Upvotes: 0
Reputation: 249532
If you don't actually need the timeout, you can get similar functionality without it by calling wait()
instead of wait_for()
.
If the only purpose of all this is to have one thread tell the other one when to stop running, a typical thing to do is to check an atomic bool at the top of the loop (while (runnable)
) and toggle it from the other thread when you want the loop to exit.
Upvotes: 4