From: SER Date: 2004-10-09T01:44:45+09:00 Subject: Re: Problems with my first multithreaded Rupy programm A couple of things. 1) Create the Waiter last. He's got the lock on the mutex, so none of the other threads can enter their synchronized sections, so they can't wait. They're simply blocking while waiting to enter the protected section. It doesn't matter that the waiter is sleep()ing, because he still has the lock. If you create the waiter last, then everybody else has a chance to enter a synchronized section and wait (releasing the lock) before the waiter does his thing. 2) Don't use Thread.stop. It isn't doing what I think you think it is doing. 3) Create another CV just for the waiter, so you can send signals directly to him 4) Wrap your "whiles" in the synchronized sections, not the other way around. For one thing, entering synchronized sections are costly; you want to do it as rarely as you can. For another, when you wait(), you release the lock, allowing others to get at the lock, so it is OK to put the while inside the synchronized section. 5) If I was going to clean up the code for you, I'd recommend doing something like this: Here's a version that works: #!/usr/bin/env ruby require 'thread' # For synchronisation mutex = Mutex.new cv = ConditionVariable.new waiter_cv = ConditionVariable.new # items on the table: # 0: cup # 1: water # 2: tea table = [] # Mike has a cup (0) mike = Thread.new { mutex.synchronize { while true cv.wait(mutex) if table.include?(1) and table.include?(2) puts "MIKE: Water and tea on table. I'll cook my tea." table = [] #sleep 3 puts "MIKE: Drinking my tea. Calling the waiter." # Wake up the waiter-Thread waiter_cv.signal #sleep 3 puts "MIKE: Now I will read my newspaper." else puts "MIKE: Not what I need." end end } } # Al has water (1) al = Thread.new { mutex.synchronize { while true cv.wait(mutex) if table.include?(0) and table.include?(2) puts "AL: Cup and tea on table. I'll cook my tea." table = [] #sleep 3 puts "AL: Drinking my tea. Calling the waiter." # Wake up the waiter-Thread waiter_cv.signal #sleep 3 puts "AL: Now I will read my newspaper." else puts "AL: Not what I need." end end } } # Ed has tea (2) ed = Thread.new { mutex.synchronize { while true cv.wait(mutex) if table.include?(0) and table.include?(1) puts "ED: Cup and water on table. I'll cook my tea." table = [] #sleep 3 puts "ED: Drinking my tea. Calling the waiter." # Wake up the waiter-Thread waiter_cv.signal #sleep 3 puts "ED: Now I will read my newspaper." else puts "ED: Not what I need." end end } } waiter = Thread.new { mutex.synchronize { while true puts "#"*80 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 the waiting tea drinkers puts "WAITER: Broadcast" cv.broadcast puts "WAITER: Wait" waiter_cv.wait(mutex) end } } waiter.join And here's a cleaner, shorter version: #!/usr/bin/env ruby require 'thread' # For synchronisation mutex = Mutex.new cv = ConditionVariable.new waiter_cv = ConditionVariable.new # items on the table: # 0: cup # 1: water # 2: tea table = [] items = %w{ Cup Water Tea } [ [:Mike,1,2], [:Al,0,2], [:Ed,0,1] ].each do |guy, *needs| Thread.new do puts "Doing #{guy}" mutex.synchronize do while true cv.wait(mutex) if (table & needs).size == 2 puts "#{guy}: #{items[table[0]]} and #{items[table[1]]} on table. "+ "I'll cook my tea." table = [] puts "#{guy}: Drinking my tea. Calling the waiter." # Wake up the waiter-Thread waiter_cv.signal puts "#{guy}: Now I will read my newspaper." else puts "#{guy}: Not what I need." end end end end end waiter = Thread.new do mutex.synchronize do while true puts "#"*80 puts "WAITER: Looking at the table!" if table.length == 0 puts "WAITER: Oh, nothing on the table!" while table[0] == table[1] do table = [rand(3), rand(3)] end puts "WAITER: Putting #{items[table[0]]} and #{items[table[1]]} on the table." else puts "WAITER: Why did you call me???" end # Call the waiting tea drinkers puts "WAITER: Broadcast" cv.broadcast puts "WAITER: Wait" waiter_cv.wait(mutex) end end end waiter.join --- SER