Установить блок while, чтобы остановить ожидание, если закончится второй поток (см. Подробнее здесь ):
def run_test
mutex = Mutex.new
cond = ConditionVariable.new
cond_main = ConditionVariable.new
threads = []
spawned = false
t1_done = false
t2_done = false
threads << Thread.new do
mutex.synchronize do
while(!spawned) do
cond.wait(mutex, 2)
end
raise 'timeout waiting for switch' if !t2_done
# some work
t1_done = true
cond.signal
end
end
threads << Thread.new do
mutex.synchronize do
spawned = true
cond.signal
# some work
t2_done = true
cond.wait(mutex, 2)
raise 'timeout waiting for switch' if !t1_done
end
end
threads.map(&:join)
end
50000.times { |x|
puts x
run_test
}
В качестве альтернативы, используя семафор подсчета , мы можемназначить потокам некоторые приоритеты:
require 'concurrent-ruby'
def run_test
mutex = Mutex.new
sync = Concurrent::Semaphore.new(0)
cond = ConditionVariable.new
cond_main = ConditionVariable.new
threads = []
t1_done = false
t2_done = false
threads << Thread.new do
mutex.synchronize do
sync.release(1)
# this needs to happen first
cond.wait(mutex, 2)
raise 'timeout waiting for switch' if !t2_done
# some work
t1_done = true
cond.signal
end
end
threads << Thread.new do
sync.acquire(1)
mutex.synchronize do
cond.signal
# some work
t2_done = true
cond.wait(mutex, 2)
raise 'timeout waiting for switch' if !t1_done
end
end
threads.map(&:join)
end
50000.times { |x|
puts x
run_test
}
Я предпочитаю второе решение, так как оно позволяет вам контролировать порядок ваших потоков, хотя и выглядит немного грязнее.
Как любопытство,в Ruby 2.6 ваш код не вызывает исключений (проверено> 10M запусков).