在Ruby中实现同步障碍

时间:2012-12-07 15:14:47

标签: ruby multithreading thread-synchronization fibers

我试图在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 }

我需要使用哪些工具来完成此任务?我尝试使用全局哈希,然后休眠,直到所有线程都设置了一个标志,表明它们已完成代码的第一部分。我无法让它正常工作;它导致了挂起和死锁。我认为我需要使用MutexConditionVariable的组合,但我不确定为什么/如何。

编辑: 50次观看,无人接听!看起来像是赏金的候选人......

2 个答案:

答案 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)

让线程彼此等待可能是有好处的。但我认为让线程实际上在“中点”完成是更清晰的,因为你的问题显然是线程需要彼此在“中点”的结果。清洁设计解决方案是让他们完成,交付他们的工作成果,并基于这些创建一组全新的线程。