Markus
10/15/2004 4:28:00 PM
I'd suggested this before (as the solution to your first problem) but it
seemed to have gotten missed--the nesting of your looping/locking needs
to be swapped. In the present case, reversing both of the "
mutex.synchronize"/"while true" pairs (and their associated "}"/"end"
pairs) should fix the problem.
-- Markus
P.S. If the reason isn't clear, ask yourself when/under what condition
(in the code as posted) did it ever leave the synchronize block? If it
still isn't clear after pondering that, post again & I'll give you a
more detailed answer.
On Fri, 2004-10-15 at 05:39, Tassilo Horn wrote:
> Hi,
>
> I still have some problems with my teadrinker app. After I implemented
> the things SER told me in
>
> Message-ID: <1097253853.581059.173460@z14g2000cwz.googlegroups.com>
>
> the program works very good for 3 drinkers.
>
> Now I wanted it to support more than 3 drinker Threads.
>
> Here's the code:
>
> <----- CODE ----->
> #! /usr/bin/env ruby
>
> require 'thread'
>
> # For synchronisation
> mutex = Mutex.new
> drinker_cv = ConditionVariable.new
> waiter_cv = ConditionVariable.new
>
> # items on the table:
> # 0: cup
> # 1: water
> # 2: tea
> table = []
> items = %w{ Cup Water Tea }
>
> # min. 3 drinkers.
> if ARGV[0].to_i >= 3
> drinkers_count = ARGV[0].to_i
> else
> drinkers_count = 3
> end
>
> drinkers_count.times do |i|
> case i%3
> when 0
> # this drinker has water and tea
> needs = [1,2]
> when 1
> # this drinker has a cup and tea
> needs = [0,2]
> when 2
> # this drinker has a cup and water
> needs = [0,1]
> end
> Thread.new {
> puts "Starting Drinker#{i}"
> mutex.synchronize {
> while true
> drinker_cv.wait(mutex)
> # The things on the table are the things the waiter needs.
> if needs.sort == table.sort
> puts "Drinker#{i}: #{items[table[0]]} and
> #{items[table[1]]} on table. I'll cook my tea!"
> # The Drinker took the items from the table. Now it's empty
> # till the waiter puts new items on it.
> table = []
> sleep 1 # (1) #
> puts "Drinker#{i}: Drinking my tea. I call the waiter again."
> # Wake up the waiter Thread
> waiter_cv.signal
> sleep 1 # (2) #
> puts "Drinker#{i}: Now I'll read my newspaper."
> end
> end
> }
> }
> end
>
> waiter = Thread.new {
> mutex.synchronize {
> while true
> puts "WAITER: Looking at the table!"
> if table.length == 0
> puts "WAITER: Oh, nothing on the table!"
> #sleep 2
> while table[0] == table[1] do
> table = [rand(3), rand(3)]
> end
> $stdout << "WAITER: Putting " << table[0]
> << " and " << table[1] << " on the table.\n"
> else
> puts "WAITER: Why did you call me???"
> end
> # Call waiting tea drinkers.
> drinker_cv.broadcast # (3) #
> puts "WAITER: Waiting for new appointments..."
> waiter_cv.wait(mutex)
> end
> }
> }
>
> waiter.join
> <----- END_OF_CODE ----->
>
> If I start it with let's say 20 drinkers (./TeaRoom.rb 20) it starts all
> threads, but only the drinkers 0..2 cook their tea. If the table
> contains the items 1 and 2 always drinker0 will cook his tea although
> drinker3, drinker6, drinker9 etc need the same items.
>
> Why is it the way it is and how do I get it the way I want which means
> that the probability of using drinkerX is the same as using drinkerX+-3?
> Why does drinker_cv.broadcast (line marked with '# (3) #') always wake
> up the same threads?
>
> One interesting this is that if I remove the lines '# (2) #' and '# (2)
> #' (the sleep() method calls) more drinkers get their turn. But still
> there are drinkers who never drink a tea and others who extremely often
> have to drink.
>
> Can anybody help me before any of my tearoom customers die with thirst
> or because of too much tea?
>
> Thanks,
> Tassilo