9

我试图__synchtreads()在 Ruby 中“复制”CUDA 函数的行为。具体来说,我有一组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 }

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

编辑: 50 次观看,没有答案!看起来像一个赏金的候选人......

4

2 回答 2

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 个是 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?

事实上,有一个名为 barrier 的 gem和我上面描述的完全一样。

最后一点,在这种情况下不要使用 sleep 来等待。这称为忙等待被认为是一种不好的做法

于 2012-12-07T18:53:46.533 回答
0

让线程相互等待可能有好处。但我认为让线程实际上在“中点”完成更干净,因为您的问题显然暗示线程需要在“中点”获得彼此的结果。干净的设计解决方案是让他们完成,交付他们的工作结果,并在这些基础上启动一组全新的线程。

于 2012-12-08T04:04:28.857 回答