Reputation: 63728
I have what seems a fairly simple need, but I'm new to using std::thread and am not sure I understand it right.
My thread's job is to run a loop: wait until an object needs processing, then process it, then wait, ...
I was about to implement this using a condition_variable
before I realised that while the thread is sat idly waiting for a new object, it won't notice a stopThread
flag has been set.
I effectively want a way to do wait_for_either(new_data,exit_thread)
but am unsure how to elegantly implement this. Older code of similar queue function uses Windows API WaitForMultipleObjects
but I want to use this as an opportunity to learn the C++11 way.
Upvotes: 0
Views: 514
Reputation: 275385
Here is a robust data consuming loop with abort options:
while(true) {
decltype(dataContainer) data;
{
std::unique_lock<std::mutex> lock(mutex);
cond.wait(lock, [] () { return stopThread || !dataContainer.empty(); });
if (stopThread)
return; //exit thread
data = std::move(dataContainer);
}
for (auto&& d:data) {
if (stopThread) return; // abort
//process data from d
}
}
stopThread
should be atomic
or access in the for(:)
loop at the bottom needs be guarded with a mutex
.
The access to stopThread
in the for(:)
loop is optional; without it, it won't abort until it finishes working on the bundle of work it picked up.
dataContainer
is a bundle of work to do in the form of a std::vector
or somesuch. The thread wakes up, grabs all the work to-do, then works on it.
You could also pop one task from dataContainer
instead of taking them all. The resulting code is a touch simpler.
To queue data into dataContainer
, you have to lock the mutex
, put the data in, then notify:
{
std::unique_lock<std::mutex> lock(mutex);
dataContainer.push_back(new_data);
}
cond.notify_one();
to shut down:
{
std::unique_lock<std::mutex> lock(mutex);
stopThread = true;
}
cond.notify_all();
Note that even if stopThread
is atomic, you need to acquire the mutex. There is otherwise a race condition.
Upvotes: 1
Reputation: 98358
When you wait for data to process, you are actually waiting for the condition variable to be signalled. So just signal the condition variable when you want to exit the thread, just as if the stopThread
flag were a special data to be processed.
The code might look like:
void thread_func()
{
std::unique_lock<std::mutex> lock(mutex);
for (;;)
{
cond.wait(lock, [] () { return stopThread || !dataContainer.empty(); });
if (stopThread)
return; //exit thread
//process data from dataContainer
}
}
To insert data:
{
std::unique_lock<std::mutex> lock(mutex);
dataContainer.push_back(new_data);
cond.notify_all();
}
And then, when you want to stop the thread:
{
std::unique_lock<std::mutex> lock(mutex);
stopThread = true;
cond.notify_all();
}
thread.join(); //not necessary but probably a good idea
Upvotes: 4
Reputation: 26486
Semi - pseudo code.
std::atomic_bool programRunning;
std::condition_variable cv;
std::mutex mtx;
std::thread loopThread([&]{
while(programRunning.load()){
std::unique_lock<std::mutex> lock(mtx);
if (newDataAvailable){
//process new data
} else{
cv.wait(lock,[&]{ return dataAvailable || !progamRunning.load(); });
}
}
});
{
std::lock_guard<std::mutex> lock(mtx);
queueMoreData();
cv.notify_one();
}
//on exit:
programRunning.store(false);
cv.notify_one();
Upvotes: 0