I wrote the following program for alternatively incrementing and doubling a counter(increment first) using boost condition variables. Can any one tell me if this is the correct use of boost condition variables. It is working correctly. I don’t understand the use of lock in wait function call. What does condition.wait(lock) mean? For example what is the use of two scoped locks in increment and multiply in this program. How can I avoid them?
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition_variable.hpp>
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int counter=0;
boost::mutex m1,m2;
bool incremented=false,multiplied=false;
boost::condition_variable c1,c2;
void Increment()
{
{
boost::mutex::scoped_lock lk(m1);
counter++;
incremented = true;
c1.notify_one();
while(!multiplied)
c2.wait(lk);
multiplied=false;
}
}
void Multiply()
{
{
boost::mutex::scoped_lock lk(m2);
while(!incremented)
c1.wait(lk);
incremented = false;
counter = counter*2 ;
multiplied = true;
c2.notify_one();
}
}
void IncrementNtimes(int n){
for(int i=0;i<n;i++){
Increment();
}
}
void MultiplyNtimes(int n){
for(int i=0;i<n;i++){
Multiply();
}
}
int main(int argc, char* argv[])
{
srand ( time(NULL) );
boost::thread thrd1(boost::bind(&IncrementNtimes,20));
boost::thread thrd2(boost::bind(&MultiplyNtimes,20));
thrd1.join();
thrd2.join();
cout<<"Main counter is:"<<counter<<endl;
return 0;
}
No, this is not correct. You’re almost there, but the big problem is that the Multiply and Increment functions should be using the same mutex.
A mutex is an object that provides MUTual EXclusion. In other words, the point of a mutex is to prevent two threads from touching the same variable at the same time and causing unpredictable results. A mutex is kind of like a token that one thread at a time holds that gives it the “right” to access a certain variable (or set of variables). In this case, the variable you are trying to protect is
counter. There must be one and only one mutex that controls the right to accesscounter. In your case, each thread will hold its own token that it thinks gives it the right to access counter, and so there will be unpredictable behavior.You “hold” a mutex by locking it. That’s the point of the locks, and that’s why you cannot “avoid” them. The entire point of the scoped locks is that, assuming you have only one mutex
m, when one of the threads holds the lock onm, the other thread is guaranteed to not also be holding a lock onm. If you’ve coded correctly, holding a lock onmshould be a prerequisite for accessingcounter, and so the value ofcountershould be predictable.Now, regarding the
wait(). A call towait()means “I give up the lock on this mutex until someone else signals this condition, and then I want it back”. In the mean time, the thread stops. So assuming you have only one mutexmand a conditionc, andlkis a lock onm, the linec.wait(lk)means that the thread will give up the locklkonmand then suspend execution until some other thread callsc.notify_one()(orc.notify_all()). When the waiting thread returns from the call towait(), it will have automatically re-gained the locklkonmand so is permitted to accesscounteragain.Finally, these boost locks are “scoped” locks. This means that they are released automatically on destruction (when they go out of scope). So in this case, each function holds its lock until it exits, except for times when it has given up its lock to wait and has suspended execution pending a signal.