From: Markus Date: 2004-10-16T01:27:56+09:00 Subject: Re: Again some problem with my multithreaded teadrinker app 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