让我们实现一个同步屏障。它必须知道它将处理的线程数,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 线程并使它们向数组添加元素。首先他们添加零,然后同步并添加1。预期结果是一个包含 2k 个元素的排序数组,其中 1k 个是 0,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?
事实上,有名为屏障的宝石这正是我上面描述的。
最后一点,在这种情况下不要使用睡眠来等待。它被称为忙着等待 and 被认为是一种不好的做法.