Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
I''m trying to implement a baton passing mechanism using threads::shared, and either I am misunderstanding the semantics of the locking and signalling primitives or it would not seem possible to reliably implement this using them.
The mechanism is analogous to the passing of the baton in a relay race. There is a single shared scalar, and each thread blocks on that scalar until it signalled and contains that threads id.
The identified thread then has the baton, and does whatever it does until it sets the id of some other thread into the baton variable, signals it, and then goes back to waiting.
This is how I think it should be coded.
use strict; use warnings; use Time::HiRes 'sleep'; use threads; use threads::shared; my $baton:shared = 0; sub sprinter { my ($no_of_threads,$sleep) = @_; my $id = threads->self->tid; warn ("Thread $id started\n"); while (1) { lock ($baton); cond_wait ($baton) until $baton == $id; warn ("Thread $id has the baton\n"); sleep ($sleep); $baton = int (rand ($no_of_threads)) until $baton != $id; warn ("Thread $id passing the baton to thread $baton\n"); cond_signal ($baton); # cond_broadcast ($baton); } } my ($no_of_sprinters, $sleep ) = @ARGV; $no_of_sprinters ||= 3; my @threads; push @threads,threads->new (\&sprinter,$no_of_sprinters, $sleep) for 1..$no_of_sprinters; sleep 1; while(1){ lock ($baton); cond_wait ($baton) until $baton == 0; warn ("Thread 0 has the baton\n"); sleep ($sleep); $baton = int (rand ($no_of_sprinters)) until $baton != 0; warn ("Thread 0 passing the baton to thread $baton\n"); cond_signal ($baton); # cond_broadcast ($baton); }
If you run this a few times, it will sometimes never pass the baton. Sometimes pass it few times and then hang.
It never seems to pass the baton more that the number of threads specified, though it will sometimes successfully pass it to the same thread two or more times before stopping.
I have found what appears to be a reliable cure (with a caveat). If you switch from using cond_signal to cond_broadcast, with no other changes it seems to run reliably for as long as you choose to leave it.
The question is, why is it necessary to use cond_broadcast? Or, what changes are required to make the cond_signal version work?
The caveat I mentioned is that whilst you can reduce the sleep value to 0.0, if you comment out the sleeps entirely, even the cond_broadcast version hangs almost at once?
This may be something to do with the note in the pod that says <cite> If there are no threads blocked in a cond_wait on the variable, the signal is discarded. By always locking before signaling, you can (with care), avoid signaling before another thread has entered cond_wait(). </cite>
I am always locking before signalling, so what extra "care" do I need to take?
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: baton passing threads and cond_signal
by ikegami (Patriarch) on Aug 21, 2007 at 20:00 UTC | |
|
Re: baton passing threads and cond_signal
by Joost (Canon) on Aug 21, 2007 at 19:27 UTC | |
by Anonymous Monk on Aug 21, 2007 at 21:40 UTC | |
by Joost (Canon) on Aug 21, 2007 at 21:53 UTC | |
by Anonymous Monk on Aug 22, 2007 at 02:55 UTC | |
by Joost (Canon) on Aug 22, 2007 at 13:11 UTC | |
| |
by Joost (Canon) on Aug 22, 2007 at 17:02 UTC |