MonitorMixin条件变量 - >僵局

时间:2012-06-01 13:15:45

标签: ruby multithreading deadlock

我有一个提供条件变量的同步队列。

当数据添加到队列时,该条件变量发出信号。

我有5个帖子:

Thread.new do
  loop do
    @queue.synchronize {
      cond.wait_until { @queue.has_data? || @queue.finished? }
    }
    # some processing code that can also call @queue.enqueue
  end
end

然后我这样做:

@queue.enqueue some_data
@threads.each(&:join)

MyQueue#enqueue看起来像这样:

def enqueue(data)
  synchronize do
    @pending << v unless queued?(data) || processed?(data) || processing?(data)
    data_cond.signal
  end
end

def finished?
  @started && @processing.empty? && @pending.empty?
end

def has_data?
  !@pending.empty?
end

我开始#join

deadlock detected

这究竟是如何导致死锁的,以及如何修复它?

3 个答案:

答案 0 :(得分:2)

我想知道这是否存在 所有 线程在 条件变量上被阻止的问题,并没有可用于排队数据的线程,这将释放其他线程。

根据此代码中的评论:

Thread.new do
  loop do
    @queue.synchronize {
      cond.wait_until { @queue.has_data? || @queue.finished? }
    }
    # some processing code that can also call @queue.enqueue
  end
end

你的评论中提到“一些也可以调用@ queue.enqueue的处理代码”,这是唯一一个调用@queue.enqueue的地方吗?如果是这样,那么所有的线程都将在条件变量上被阻塞,并且没有人能够到达能够调用enqueue的点。我确信Ruby可以检测到所有线程都锁定在同一个实体上,并且没有一个可以释放它,因此死锁。

如果确实有一个单独的线程只排队(这将是典型的生产者/消费者情况),请确保它不会等待条件变量,这也可能导致死锁。

答案 1 :(得分:1)

帮助你有点困难,因为你只发布代码片段......

你应该试试work_queue宝石,或者至少看看source code

答案 2 :(得分:0)

没有必要等待has_data? ||完了吗?在同步块中。代码应如下所示:

Thread.new do
  loop do
    cond.wait_until { @queue.has_data? || @queue.finished? }
    enq = nil
    @queue.synchronize {
        enq = @queue.pop
    }
    # some processing code that can also call @queue.enqueue
  end
end

在这种情况下,只有在使用队列内容进行操作时才会锁定其他线程。您需要做的是同步队列状态更改,如完成

更好的解决方案是使用互斥锁包装所有线程关键变量,例如rails中的here。它会使代码变慢,因为它消除了同时变量访问。