First of all: I am completely a newbie in mutex/multithread programming, so
sorry for any error in advance...
I have a program that runs multiple threads. The threads (usually one per
cpu core) do a lot of
calculation and "thinking" and then sometimes they decide to call a
particular (shared) method that updates some statistics.
The concurrency on statistics updates is managed through the use of a mutex:
stats_mutex.lock();
common_area->update_thread_stats( ... );
stats_mutex.unlock();
Now to the problem.
Of all those threads there is one particular thread that need almost
realtime priority, because it's the only thread that actually operates.
With "almost realtime priority" I mean:
Let's suppose thread t0 is the "privileged one" and t1....t15 are the normal
ones.What happens now is:
Thread t1 acquires lock.
Thread t2, t3, t0 call the lock() method and wait for it to succeed.
Thread t1 calls unlock()
One (at random, as far as i know) of the threads t2, t3, t0 succeeds in acquiring
the lock, and the other ones continue to wait.
What I need is:
Thread t1 acquire lock.
Thread t2, t3, t0 call the lock() method and wait for it to succeed.
Thread t1 calls unlock()
Thread t0 acquires lock since it's privileged
So, what's the best (possibly simplest) method to do this thing?
What I was thinking is to have a bool variable called
"privileged_needs_lock".
But I think I need another mutex to manage access to this variable... I dont
know if this is the right way...
Additional info:
my threads use C++11 (as of gcc 4.6.3)
code needs to run on both Linux and Windows (but tested only on Linux at the moment).
performance on locking mechanism is not an issue (my performance problem are in internal thread calculations, and thread number will always be low, one or two per cpu core at maximum)
Any idea is appreciated.
Thanks
The below solution works (three mutex way):
#include <thread>
#include <iostream>
#include <mutex>
#include "unistd.h"
std::mutex M;
std::mutex N;
std::mutex L;
void lowpriolock(){
L.lock();
N.lock();
M.lock();
N.unlock();
}
void lowpriounlock(){
M.unlock();
L.unlock();
}
void highpriolock(){
N.lock();
M.lock();
N.unlock();
}
void highpriounlock(){
M.unlock();
}
void hpt(const char* s){
using namespace std;
//cout << "hpt trying to get lock here" << endl;
highpriolock();
cout << s << endl;
sleep(2);
highpriounlock();
}
void lpt(const char* s){
using namespace std;
//cout << "lpt trying to get lock here" << endl;
lowpriolock();
cout << s << endl;
sleep(2);
lowpriounlock();
}
int main(){
std::thread t0(lpt,"low prio t0 working here");
std::thread t1(lpt,"low prio t1 working here");
std::thread t2(hpt,"high prio t2 working here");
std::thread t3(lpt,"low prio t3 working here");
std::thread t4(lpt,"low prio t4 working here");
std::thread t5(lpt,"low prio t5 working here");
std::thread t6(lpt,"low prio t6 working here");
std::thread t7(lpt,"low prio t7 working here");
//std::cout << "All threads created" << std::endl;
t0.join();
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
return 0;
}
Tried the below solution as suggested but it does not work (compile with " g++ -std=c++0x -o test test.cpp -lpthread"):
#include <thread>
#include <mutex>
#include "time.h"
#include "pthread.h"
std::mutex l;
void waiter(){
l.lock();
printf("Here i am, waiter starts\n");
sleep(2);
printf("Here i am, waiter ends\n");
l.unlock();
}
void privileged(int id){
usleep(200000);
l.lock();
usleep(200000);
printf("Here i am, privileged (%d)\n",id);
l.unlock();
}
void normal(int id){
usleep(200000);
l.lock();
usleep(200000);
printf("Here i am, normal (%d)\n",id);
l.unlock();
}
int main(){
std::thread tw(waiter);
std::thread t1(normal,1);
std::thread t0(privileged,0);
std::thread t2(normal,2);
sched_param sch;
int policy;
pthread_getschedparam(t0.native_handle(), &policy, &sch);
sch.sched_priority = -19;
pthread_setschedparam(t0.native_handle(), SCHED_FIFO, &sch);
pthread_getschedparam(t1.native_handle(), &policy, &sch);
sch.sched_priority = 18;
pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch);
pthread_getschedparam(t2.native_handle(), &policy, &sch);
sch.sched_priority = 18;
pthread_setschedparam(t2.native_handle(), SCHED_FIFO, &sch);
tw.join();
t1.join();
t0.join();
t2.join();
return 0;
}
I can think of three methods using only threading primitives:
Triple mutex
Three mutexes would work here:
data mutex ('M')
next-to-access mutex ('N'), and
low-priority access mutex ('L')
Access patterns are:
Low-priority threads: lock L, lock N, lock M, unlock N, { do stuff }, unlock M, unlock L
High-priority thread: lock N, lock M, unlock N, { do stuff }, unlock M
That way the access to the data is protected, and the high-priority thread can get ahead of the low-priority threads in access to it.
Mutex, condition variable, atomic flag
The primitive way to do this is with a condition variable and an atomic:
Mutex M;
Condvar C;
atomic bool hpt_waiting;
Data access patterns:
Low-priority thread: lock M, while (hpt_waiting) wait C on M, { do stuff }, broadcast C, unlock M
High-priority thread: hpt_waiting := true, lock M, hpt_waiting := false, { do stuff }, broadcast C, unlock M
Mutex, condition variable, two non-atomic flag
Alternatively you can use two non-atomic bools with a condvar; in this technique the mutex/condvar protects the flags, and the data is protected not by a mutex but by a flag:
Mutex M;
Condvar C;
bool data_held, hpt_waiting;
Low-priority thread: lock M, while (hpt_waiting or data_held) wait C on M, data_held := true, unlock M, { do stuff }, lock M, data_held := false, broadcast C, unlock M
High-priority thread: lock M, hpt_waiting := true, while (data_held) wait C on M, data_held := true, unlock M, { do stuff }, lock M, data_held := false, hpt_waiting := false, broadcast C, unlock M
Put requesting threads on a 'priority queue'. The privileged thread can get first go at the data when it's free.
One way to do this would be withan array of ConcurrentQueues[privilegeLevel], a lock and some events.
Any thread that wants at the data enters the lock. If the data is free, (boolean), it gets the data object and exits the lock. If the data is in use by another thread, the requesting thread pushes an event onto one of the concurrent queues, depending on its privilege level, exits the lock and waits on the event.
When a thread wants to release its ownership of the data object, it gets the lock and iterates the array of ConcurrentQueues from the highest-privilege end down, looking for an event, (ie queue count>0). If it finds one, it signals it and exits the lock, if not, it sets the 'dataFree' boolean and and exits the lock.
When a thread waiting on an event for access to the data is made ready, it may access the data object.
I thnk that should work. Please, other developers, check this design and see if you can think of any races etc? I'm still suffering somewhat from 'hospitality overload' after a trip to CZ..
Edit - probably don't even need concurrent queues because of the explicit lock across them all. Any old queue would do.
#include <thread>
#include <mutex>
#include <condition_variable>
#include <cassert>
class priority_mutex {
std::condition_variable cv_;
std::mutex gate_;
bool locked_;
std::thread::id pr_tid_; // priority thread
public:
priority_mutex() : locked_(false) {}
~priority_mutex() { assert(!locked_); }
priority_mutex(priority_mutex&) = delete;
priority_mutex operator=(priority_mutex&) = delete;
void lock(bool privileged = false) {
const std::thread::id tid = std::this_thread::get_id();
std::unique_lock<decltype(gate_)> lk(gate_);
if (privileged)
pr_tid_ = tid;
cv_.wait(lk, [&]{
return !locked_ && (pr_tid_ == std::thread::id() || pr_tid_ == tid);
});
locked_ = true;
}
void unlock() {
std::lock_guard<decltype(gate_)> lk(gate_);
if (pr_tid_ == std::this_thread::get_id())
pr_tid_ = std::thread::id();
locked_ = false;
cv_.notify_all();
}
};
NOTICE: This priority_mutex provides unfair thread scheduling. If privileged thread acquires the lock frequently, other non-privileged threads may almost not scheduled.
Usage example:
#include <mutex>
priority_mutex mtx;
void privileged_thread()
{
//...
{
mtx.lock(true); // acquire 'priority lock'
std::unique_lock<decltype(mtx)> lk(mtx, std::adopt_lock);
// update shared state, etc.
}
//...
}
void normal_thread()
{
//...
{
std::unique_lock<decltype(mtx)> lk(mtx); // acquire 'normal lock'
// do something
}
//...
}
On linux you can check this man: pthread_setschedparam and also man sched_setscheduler
pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param *param);
Check this also for c++2011:
http://msdn.microsoft.com/en-us/library/system.threading.thread.priority.aspx#Y78
pthreads has thread priorities:
pthread_setschedprio( (pthread_t*)(&mThreadId), wpri );
If multiple threads are sleeping waiting in a lock, the scheduler will wake the highest priority thread first.
Try something like the following. You could make the class a thread-safe singleton and you could even make it a functor.
#include <pthread.h>
#include <semaphore.h>
#include <map>
class ThreadPrioFun
{
typedef std::multimap<int, sem_t*> priomap_t;
public:
ThreadPrioFun()
{
pthread_mutex_init(&mtx, NULL);
}
~ThreadPrioFun()
{
pthread_mutex_destroy(&mtx);
}
void fun(int prio, sem_t* pSem)
{
pthread_mutex_lock(&mtx);
bool bWait = !(pm.empty());
priomap_t::iterator it = pm.insert(std::pair<int, sem_t*>(prio, pSem) );
pthread_mutex_unlock(&mtx);
if( bWait ) sem_wait(pSem);
// do the actual job
// ....
//
pthread_mutex_lock(&mtx);
// done, remove yourself
pm.erase(it);
if( ! pm.empty() )
{
// let next guy run:
sem_post((pm.begin()->second));
}
pthread_mutex_unlock(&mtx);
}
private:
pthread_mutex_t mtx;
priomap_t pm;
};
Since thread priorities isn't working for you:
Create 2 mutexes, a regular lock and a priority lock.
Regular threads must first lock the normal lock, and then the priority lock. The priority thread only has to lock the priority lock:
Mutex mLock;
Mutex mPriLock;
doNormal()
{
mLock.lock();
pthread_yield();
doPriority();
mLock.unlock();
}
doPriority()
{
mPriLock.lock();
doStuff();
mPriLock.unlock();
}
Modified slightly ecatmur answer, adding a 4th mutex to handle multiple high priority threads contemporaneously (note that this was not required in my original question):
#include <thread>
#include <iostream>
#include "unistd.h"
std::mutex M; //data access mutex
std::mutex N; // 'next to access' mutex
std::mutex L; //low priority access mutex
std::mutex H; //hptwaiting int access mutex
int hptwaiting=0;
void lowpriolock(){
L.lock();
while(hptwaiting>0){
N.lock();
N.unlock();
}
N.lock();
M.lock();
N.unlock();
}
void lowpriounlock(){
M.unlock();
L.unlock();
}
void highpriolock(){
H.lock();
hptwaiting++;
H.unlock();
N.lock();
M.lock();
N.unlock();
}
void highpriounlock(){
M.unlock();
H.lock();
hptwaiting--;
H.unlock();
}
void hpt(const char* s){
using namespace std;
//cout << "hpt trying to get lock here" << endl;
highpriolock();
cout << s << endl;
usleep(30000);
highpriounlock();
}
void lpt(const char* s){
using namespace std;
//cout << "lpt trying to get lock here" << endl;
lowpriolock();
cout << s << endl;
usleep(30000);
lowpriounlock();
}
int main(){
std::thread t0(lpt,"low prio t0 working here");
std::thread t1(lpt,"low prio t1 working here");
std::thread t2(hpt,"high prio t2 working here");
std::thread t3(lpt,"low prio t3 working here");
std::thread t4(lpt,"low prio t4 working here");
std::thread t5(lpt,"low prio t5 working here");
std::thread t6(hpt,"high prio t6 working here");
std::thread t7(lpt,"low prio t7 working here");
std::thread t8(hpt,"high prio t8 working here");
std::thread t9(lpt,"low prio t9 working here");
std::thread t10(lpt,"low prio t10 working here");
std::thread t11(lpt,"low prio t11 working here");
std::thread t12(hpt,"high prio t12 working here");
std::thread t13(lpt,"low prio t13 working here");
//std::cout << "All threads created" << std::endl;
t0.join();
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
t9.join();
t10.join();
t11.join();
t12.join();
t13.join();
return 0;
}
What do you think? Is it ok? It's true that a semaphore could handle better this kind of thing, but mutexes are much more easy to manage to me.
Related
Lets say I have one thread that continuously updates a certain object. During the update, the object must be locked for thread safety.
Now the second thread is more of an event kind of operation. If such a thread is spawned, I'd like the running update to finish it's call and then immediately perform the event operation.
What I absolutely want to avoid is a situation where the event thread needs to wait until it gets lucky to be given computation time at a specific time the update thread doesn't lock up the data it needs to access.
Is there any way I could use the threading/mutex tools in c++ to accomplish this? Or should I save the to-be-done operation in an unlocked var and perform the operation on the update thread?
//// System.h
#pragma once
#include <mutex>
#include <iostream>
#include <chrono>
#include <thread>
class System {
private:
int state = 0;
std::mutex mutex;
public:
void update();
void reset(int e);
};
//////// System.cpp
#include "System.h"
void System::update() {
std::lock_guard<std::mutex> guard(mutex);
state++;
std::cout << state << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void System::reset(int e) {
std::lock_guard<std::mutex> guard(mutex);
state = e;
std::cout << state << std::endl;
}
////// ThreadTest.h
#pragma once
#include <iostream>
#include "System.h"
void loop_update(System& system);
void reset_system(System& system);
int main();
////// ThreadTest.cpp
#include "ThreadTest.h"
void loop_update(System& system) {
while (true) system.update();
};
void reset_system(System& system) {
system.reset(0);
};
int main()
{
System system;
std::thread t1 = std::thread(loop_update, std::ref(system));
int reset = 0;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Reset" << std::endl;
reset_system(system);
}
}
Example gives following output. You can clearly see a huge delay in the actual update.
1
...
10
Reset
11
...
16
0
1
...
10
Reset
11
...
43
0
1
If I understand you correctly, you have 2 threads using the same mutex. However, you want one thread to get a higher preference than the other to get the actual lock.
As far as I know, there ain't a way to ensure preference using the native tools. You can work around it, if you don't mind the code of both threads knowing about it.
For example:
std::atomic<int> shouldPriorityThreadRun{0};
auto priorityThreadCode = [&shouldPriorityThreadRun](){
++shouldPriorityThreadRun;
auto lock = std::unique_lock{mutex};
doMyStuff();
--shouldPriorityThreadRun;
};
auto backgroundThreadCode = [&shouldPriorityThreadRun](){
while (true)
{
if (shouldPriorityThreadRun == 0)
{
auto lock = std::unique_lock{mutex};
doBackgroundStuff();
}
else
std::this_thread::yield();
}
};
If you have multiple priority threads, those can't have priority over each other.
If you don't like the yield, you could do fancier stuff with std::condition_variable, so you can inform other threads that the mutex is available. However, I believe it's good enough.
it should already work with your current approach.
The mutex is locking concurrent access to your data, so you can lock it within the first thread to update the data.
If the event routine / your second thread comes to execution, it always has to check if the mutex is unlocked. If the mutex is unlocked - and only then, you can lock the mutex and perform the tasks of the second thread.
If I understand your code correctly (i am not a c++ expert), the std::lock_guard<std::mutex> guard(mutex); seems to be locking the mutex the entire time of the update function...
And therefore other threads merely have time to access the mutex.
When the update thread finish the job, it needs to unlock the mutex before entering the sleep state, then the reset thread could have a chance to take the lock without any delay. I also tried running your codes on my machine and observe it's still waiting for the lock. I don't know when it gets lucky to take the lock. I think in this case it's an UB
2
3
4
5
6
7
8
9
10
Reset
11
12
13
14
15
16
17
18...
void System::update() {
mutex.lock();
state++;
std::cout << state << std::endl;
mutex.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void System::reset(int e) {
mutex.lock();
state = e;
std::cout << state << std::endl;
mutex.unlock();
}
I'm trying to understand condition_variables.
I guess my code should work like:
1. main lock mx
2. main wait() notify <= here lock released
3. threads lock mx
4. threads send notify
5. threads unlock mx
6. main wait() finished and lock mx
So why threads can lock mx faster than wait() call after notify?
Example
#include <iostream>
#include <future>
#include <condition_variable>
#include <vector>
using namespace std::chrono_literals;
std::shared_future<void> ready;
std::mutex finish_mx;
std::condition_variable finish_cv;
int execute(int val, const std::shared_future<void> &ready){
ready.wait();
std::lock_guard<std::mutex> lock(finish_mx);
std::cout<<"Locked: "<<val<<std::endl;
finish_cv.notify_one();
return val;
}
int main()
{
std::promise<void> promise;
auto shared = promise.get_future().share();
std::vector<std::future<int>> pool;
for (int i=0; i<10; ++i){
auto fut = std::async(std::launch::async, execute, i, std::cref(shared));
pool.push_back(std::move(fut));
}
std::this_thread::sleep_for(100ms);
std::unique_lock<std::mutex> finish_lock(finish_mx);
promise.set_value();
for (int i=0; pool.size() > 0; ++i)
{
finish_cv.wait(finish_lock);
std::cout<<"Notifies: "<<i<<std::endl;
for (auto it = pool.begin(); it != pool.end(); ++it) {
auto state = it->wait_for(0ms);
if (state == std::future_status::ready) {
pool.erase(it);
break;
}
}
}
}
example output:
Locked: 6
Locked: 7
Locked: 8
Locked: 9
Locked: 5
Locked: 4
Locked: 3
Locked: 2
Locked: 1
Notifies: 0
Locked: 0
Notifies: 1
Edit
for (int i=0; pool.size() > 0; ++i)
{
finish_cv.wait(finish_lock);
std::cout<<"Notifies: "<<i<<std::endl;
auto it = pool.begin();
while (it != pool.end()) {
auto state = it->wait_for(0ms);
if (state == std::future_status::ready) {
/* process result */
it = pool.erase(it);
} else {
++it;
}
}
}
This depends on how your OS schedules threads that are waiting to acquire a mutex lock. All the execute threads are already waiting to acquire the mutex lock before the first notify_one, so if there's a simple FIFO queue of threads waiting to lock the mutex then they are all ahead of the main thread in the queue. As each mutex unlocks the mutex, the next one in the queue locks it.
This has nothing to do with mutexes being "faster" than condition variables, the condition variable has to lock the same mutex to return from the wait.
As soon as the future becomes ready all the execute threads return from the wait and all try to lock the mutex, joining the queue of waiters. When the condition variable starts to wait the mutex is unlocked, and one of the other threads (the one at the front of the queue) gets the lock. It calls notify_one which causes the condition variable to try to relock the mutex, joining the back of the queue. The notifying thread unlocks the mutex, and the next thread in the queue gets the lock, and calls notify_one (which does nothing because the condition variable is already notified and waiting to lock the mutex). Then the next thread in the queue gets the mutex, and so on.
It seems that one of the execute threads didn't run quickly enough to get in the queue before the first notify_one call, so it ended up in the queue behind the condition variable.
Can anyone point me at the thing I try to do in this code, because SecondLoop thread is unreachable at all? It becomes reachable only if I remove while(true) loop.
#include <iostream>
#include <thread>
using namespace std;
void Loop() {
while(true) {
(do something)
}
}
void SecondLoop() {
while(true) {
(do something)
}
}
int main() {
thread t1(Loop);
t1.join();
thread t2(SecondLoop);
t2.join(); // THIS THREAD IS UNREACHABLE AT ALL!
return false;
}
The reason why I use multithreading is because I need to get two loops running at the same time.
join blocks the current thread to wait for another thread to finish. Since your t1 never finishes, your main thread waits for it indefinitely.
Edit:
To run two threads indefinitely and concurrency, first create the threads, and then wait for both:
int main() {
thread t1(Loop);
thread t2(SecondLoop);
t1.join();
t2.join();
}
To run Loop and SecondLoop concurrency, you have to do something like:
#include <iostream>
#include <thread>
void Loop() {
while(true) {
//(do something)
}
}
void SecondLoop() {
while(true) {
//(do something)
}
}
int main() {
std::thread t1(Loop);
std::thread t2(SecondLoop);
t1.join();
t2.join();
}
as join block current thread to wait the other thread finishes.
.join() waits for the thread to end (so in this case if you break out of the while loops and exit the thread function)
using while(true) is linked to the tread running , you should look for a way to exit that loop, use some sort of loop control
Based on my comment and what #Nidhoegger answered I suggest:
int main() {
thread t1(Loop);
thread t2(SecondLoop);
// Your 2 threads will run now in paralel
// ... <- So some other things with your application
// Now you want to close the app, perhaps all work is done or the user asked it to quit
// Notify threads to stop
t1running = false;
t2running = false;
// Wait for all threads to stop
t1.join();
t2.join();
// Exit program
return false;
}
This code is simplification of real project code. Main thread create worker thread and wait with std::condition_variable for worker thread really started. In code below std::condition_variable wakes up after current_thread_state becomes "ThreadState::Stopping" - this is the second notification from worker thread, that is the main thread did not wake up after the first notification, when current_thread_state becomes "ThreadState::Starting". The result was deadlock. Why this happens? Why std::condition_variable not wake up after first thread_event.notify_all()?
int main()
{
std::thread thread_var;
struct ThreadState {
enum Type { Stopped, Started, Stopping };
};
ThreadState::Type current_thread_state = ThreadState::Stopped;
std::mutex thread_mutex;
std::condition_variable thread_event;
while (true) {
{
std::unique_lock<std::mutex> lck(thread_mutex);
thread_var = std::move(std::thread([&]() {
{
std::unique_lock<std::mutex> lck(thread_mutex);
cout << "ThreadFunction() - step 1\n";
current_thread_state = ThreadState::Started;
}
thread_event.notify_all();
// This code need to disable output to console (simulate some work).
cout.setstate(std::ios::failbit);
cout << "ThreadFunction() - step 1 -> step 2\n";
cout.clear();
{
std::unique_lock<std::mutex> lck(thread_mutex);
cout << "ThreadFunction() - step 2\n";
current_thread_state = ThreadState::Stopping;
}
thread_event.notify_all();
}));
while (current_thread_state != ThreadState::Started) {
thread_event.wait(lck);
}
}
if (thread_var.joinable()) {
thread_var.join();
current_thread_state = ThreadState::Stopped;
}
}
return 0;
}
Once you call the notify_all method, your main thread and your worker thread (after doing its work) both try to get a lock on the thread_mutex mutex. If your work load is insignificant, like in your example, the worker thread is likely to get the lock before the main thread and sets the state back to ThreadState::Stopped before the main thread ever reads it. This results in a dead lock.
Try adding a significant work load, e.g.
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
to the worker thread. Dead locks are far less likely now. Of course, this is not a fix for your problem. This is just for illustrating the problem.
You have two threads racing: one writes values of current_thread_state twice, another reads the value of current_thread_state once.
It is indeterminate whether the sequence of events is write-write-read or write-read-write as you expect, both are valid executions of your application.
I have three threads in my application, the first thread needs to wait for a data to be ready from the two other threads. The two threads are preparing the data concurrently.
In order to do that I am using condition variable in C++ as following:
boost::mutex mut;
boost::condition_variable cond;
Thread1:
bool check_data_received()
{
return (data1_received && data2_received);
}
// Wait until socket data has arrived
boost::unique_lock<boost::mutex> lock(mut);
if (!cond.timed_wait(lock, boost::posix_time::milliseconds(200),
boost::bind(&check_data_received)))
{
}
Thread2:
{
boost::lock_guard<boost::mutex> lock(mut);
data1_received = true;
}
cond.notify_one();
Thread3:
{
boost::lock_guard<boost::mutex> lock(mut);
data2_received = true;
}
cond.notify_one();
So my question is it correct to do that, or is there any more efficient way? I am looking for the most optimized way to do the waiting.
It looks like you want a semaphore here, so you can wait for two "resources" to be "taken".
For now, just replace the mutual exclusion with an atomic. you can still use a cv to signal the waiter:
#include <boost/thread.hpp>
boost::mutex mut;
boost::condition_variable cond;
boost::atomic_bool data1_received(false);
boost::atomic_bool data2_received(false);
bool check_data_received()
{
return (data1_received && data2_received);
}
void thread1()
{
// Wait until socket data has arrived
boost::unique_lock<boost::mutex> lock(mut);
while (!cond.timed_wait(lock, boost::posix_time::milliseconds(200),
boost::bind(&check_data_received)))
{
std::cout << "." << std::flush;
}
}
void thread2()
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(rand() % 4000));
data1_received = true;
cond.notify_one();
}
void thread3()
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(rand() % 4000));
data2_received = true;
cond.notify_one();
}
int main()
{
boost::thread_group g;
g.create_thread(thread1);
g.create_thread(thread2);
g.create_thread(thread3);
g.join_all();
}
Note:
warning - it's essential that you know only the waiter is waiting on the cv, otherwise you need notify_all() instead of notify_one().
It is not important that the waiter is already waiting before the workers signal their completion, because the predicated timed_wait checks the predicate before blocking.
Because this sample uses atomics and predicated wait, it's not actually critical to signal the cv under the mutex. However, thread checkers will (rightly) complain about this (I think) because it's impossible for them to check proper synchronization unless you add the locking.