Sean Russell
10/8/2004 4:43:00 PM
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:
<code lang='ruby'>
#!/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
</code>
And here's a cleaner, shorter version:
<code lang='ruby'>
#!/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
</code>
--- SER