我试图在Ruby中“复制”CUDA __synchtreads()
函数的行为。具体来说,我有一组需要执行某些代码的N
个线程,然后所有线程都在执行的中间点等待,然后继续执行其余的业务。例如:
x = 0
a = Thread.new do
x = 1
syncthreads()
end
b = Thread.new do
syncthreads()
# x should have been changed
raise if x == 0
end
[a,b].each { |t| t.join }
我需要使用哪些工具来完成此任务?我尝试使用全局哈希,然后休眠,直到所有线程都设置了一个标志,表明它们已完成代码的第一部分。我无法让它正常工作;它导致了挂起和死锁。我认为我需要使用Mutex
和ConditionVariable
的组合,但我不确定为什么/如何。
编辑: 50次观看,无人接听!看起来像是赏金的候选人......
答案 0 :(得分:8)
让我们实现同步障碍。它必须预先知道它将处理的线程数 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。
答案 1 :(得分:0)
让线程彼此等待可能是有好处的。但我认为让线程实际上在“中点”完成是更清晰的,因为你的问题显然是线程需要彼此在“中点”的结果。清洁设计解决方案是让他们完成,交付他们的工作成果,并基于这些创建一组全新的线程。