讓我們實現一個同步障礙。它必須知道它將處理的線程數量,預先處理的數量爲n。在第一個n - 1調用sync
屏障將導致調用線程等待。電話號碼n將喚醒所有線程。
class Barrier
def initialize(count)
@mutex = Mutex.new
@cond = ConditionVariable.new
@count = count
end
def sync
@mutex.synchronize do
@count -= 1
if @count > 0
@cond.wait @mutex
else
@cond.broadcast
end
end
end
end
整體sync
是一個關鍵部分,即它不能同時由兩個線程執行。因此致電Mutex#synchronize
。
當減值@count
爲正值時,線程被凍結。將互斥量作爲參數傳遞給ConditionVariable#wait
是防止死鎖的關鍵。它會在凍結線程之前解鎖互斥體。
一個簡單的實驗啓動1k個線程,並使它們添加元素到一個數組。首先他們添加零,然後他們同步和添加。預期的結果是一個有2k個元素的排序數組,其中1k是零,1k是1。
mtx = Mutex.new
arr = []
num = 1000
barrier = Barrier.new num
num.times.map do
Thread.start do
mtx.synchronize { arr << 0 }
barrier.sync
mtx.synchronize { arr << 1 }
end
end .map &:join;
# Prints true. See it break by deleting `barrier.sync`.
puts [
arr.sort == arr,
arr.count == 2 * num,
arr.count(&:zero?) == num,
arr.uniq == [0, 1],
].all?
事實上,有a gem named barrier這正是我上面描述的。
最後一點,在這種情況下不要用睡覺來等待。它叫做忙等待和is considered a bad practice。
來源
2012-12-07 18:53:46
Jan
@sawa我在上面的代碼展示中發現了這個錯誤,並且它能夠正常工作,但我願意提供更清晰的建議。睡覺()被認爲是不好的習慣? – user2398029
'睡眠'不是一個好習慣。這不是你應該完全避免的,但儘可能避免。我有這種感覺,你可以通過'Thread#join'或者使用'Fiber'。 – sawa
謝謝,我將添加'fiber'作爲標籤。 – user2398029