Ruby:Concurrent :: Semaphore产生死锁

时间:2018-09-21 05:11:34

标签: ruby deadlock semaphore

我正在测试Concurrent :: Semaphore,

require 'concurrent'

loop do
  semaphore = Concurrent::Semaphore.new(3)

  (1..5).each_with_object([]) do |_n, result|
    result << Thread.new do
      semaphore.acquire

      print '.'

      semaphore.release
    end
  end.each(&:join)
end

但是代码会在几秒钟内产生如下所示的异常。

./semaphore.rb:14:in `join': No live threads left. Deadlock? (fatal)
2 threads, 2 sleeps current:0x00007f867c9a73a0 main thread:0x00007f867a503350
* #<Thread:0x00007f867a869c20 sleep_forever>
   rb_thread_t:0x00007f867a503350 native:0x00007fffa9b16380 int:0
   ./semaphore.rb:14:in `join'
   ./semaphore.rb:14:in `each'
   ./semaphore.rb:14:in `block in <main>'
   ./semaphore.rb:3:in `loop'
   ./semaphore.rb:3:in `<main>'
* #<Thread:0x00007f867b8891f0@./semaphore.rb:7 sleep_forever>
   rb_thread_t:0x00007f867c9a73a0 native:0x000070000c9b8000 int:0
    depended by: tb_thread_id:0x00007f867a503350
   ./semaphore.rb:10:in `write'
   ./semaphore.rb:10:in `print'
   ./semaphore.rb:10:in `block (3 levels) in <main>'

用法有问题吗?

(带有MRI ruby​​ 2.5.1p57(2018-03-29修订版63029)[x86_64-darwin17])

1 个答案:

答案 0 :(得分:0)

(明显的)死锁不是直接由您使用信号量引起的。相反,这里发生的是您有两个正在阻塞的线程(两个线程都在等待完成)。

您的第一个线程确实在等待信号灯可用。

但是,第二个线程当前正在将数据写入STDOUT,在您的情况下,该线程也正在阻塞。如果读取Ruby进程STDOUT的进程(例如您的终端)的速度不足以读取所有数据,通常会发生这种情况。一旦管道的缓冲区已满,就写入STDOUT块,从而导致该线程也不活动。

Thread#join检测到此错误,从而引发了异常。

要解决此问题,您可以确保从过程的STDOUT读取足够快的内容。然后,我再也无法重现该问题。

出于文档目的:通过运行ruby ./semaphore.rb | ruby -e "sleep 30"semaphore.rb并包含问题中显示的代码,我可以始终如一地重现OP描述的问题。