Re: Blocking condition variables
Hello Rainer!
I think I see your issue -- in-line below.
On May 8, 6:11 pm, Rainer Grimm <r.gr...@science-computing.de> wrote:
...
Hello,
I have written a program, which will describe a workflow synchronized by
condition variables. The idea is simple. Two workers notify the boss
(notify_one), when their step of work is done. As soon as the boss gets
the two notifications, he notifys (notify_all) both worker, so that they
can proceed.
...
And here is the program.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
...
class Worker{
public:
Worker(std::string n):name(n){};
void operator() (){
// prepare the work and notfiy the boss
int prepareTime= getRandomTime(500,2000);
std::this_thread::sleep_for(std::chrono::milliseconds(prepareTime));
preparedCount++;
std::cout << name << ": " << prepareTime << std::endl;
worker2BossCondVariable.notify_one();
Just a comment here: This isn't your problem, but there is a
potential race condition here. (In practice, however, this sample
program, because of the sleeps in the worker threads won't have this
potential race.) The problem is that the worker thread might call
worker2BossCondVariable.notify_one();
before the parent thread waits on the condition variable, thereby
causing the parent thread to miss the notification, and not unblock.
// { scope seems to be necessary
// wait for the start notification of the boss
std::unique_lock<std::mutex> startWorkLock( startWorkMutex );
boss2WorkerCondVariable.wait( startWorkLock,[]{ return startWork;});
Here's the problem: As the condition-variable wait
boss2WorkerCondVariable.wait( startWorkLock,[]{ return
startWork;});
returns, the startWork mutex gets reacquired. Without what you term
the "artificial scope," that mutex stays acquired, blocking the second
worker thread. When you put
std::unique_lock<std::mutex> startWorkLock( startWorkMutex );
inside of the "artificial scope," then when the startWorkLock is
destroyed at the end of the "artificial scope" the mutex is
released (that's a big part of what unique_lock is used for),
unblocking the second worker thread.
// }
This is the end-of-scope that causes the unique_lock to be destroyed
and the mutex released (if the scope weren't commented out).
...
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
In case I'm executing the program, the condition variables will block:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOSS: PREPARE YOUR WORK.
Worker1: 658
Worker2: 1836
BOSS: START YOUR WORK.
Worker1: 270
^C
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
In case I'm using the artificial scope in the the functor (look at the
source code), the program behave in the expected way.
BOSS: PREPARE YOUR WORK.
Worker1: 650
Worker2: 1507
BOSS: START YOUR WORK.
Worker1: 201
Worker2: 364
BOSS: GO HOME.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
As far as I can tell the above analysis is completely consistent with
the results you see.
This behaviour is happening with GCC4.6 and also with GCC4.7. I have no
idea why is is necessary to control the lifetime of the unique_lock in
den body of the functor.
As mentioned above, it's the destruction of the unique_lock triggered
by the end of scope that releases the mutex.
I think putting scope blocks around the use of unique_lock (and
lock_guard) is a pretty standard technique.
Kindly regards from Rottenburg,
Rainer Grimm
Good luck!
K. Frank
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]