3

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

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

我有 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

这究竟是如何导致死锁的,如何解决它?

4

3 回答 3

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 可以检测到所有线程都被锁定在同一个实体上,并且没有一个可以释放它,因此死锁。

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

于 2012-06-11T10:01:50.890 回答
1

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

你应该试试work_queue gem,或者至少看看源代码

于 2012-06-11T13:55:46.437 回答
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 中一样。它会使代码变慢一点,因为它消除了同时变量访问。

于 2012-06-05T11:49:20.180 回答