Ruby:Concurrent::Semaphore 产生死锁
Ruby: Concurrent::Semaphore yields deadlock
我正在测试 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])
(明显的)死锁不是由您使用信号量直接引起的。相反,这里发生的是你有两个线程(你都在等待完成)它们都在阻塞。
您的第一个线程确实在等待信号量可用。
但是第二个线程当前正在向 STDOUT
写入数据,在您的情况下这也是阻塞的。如果读取 Ruby 进程(例如您的终端)的 STDOUT
的进程不够快,无法读取所有数据,通常会发生这种情况。一旦管道的缓冲区已满,将写入 STDOUT
块,导致线程也不会处于活动状态。
这被 Thread#join
检测到,导致抛出异常。
要解决此问题,您只需确保您从进程的 STDOUT
中读取的速度足够快。然后,我再也无法重现这个问题了。
出于文档目的:我可以通过 运行 ruby ./semaphore.rb | ruby -e "sleep 30"
一致地重现 OP 描述的问题,其中 semaphore.rb
包含问题中显示的代码。
我正在测试 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])
(明显的)死锁不是由您使用信号量直接引起的。相反,这里发生的是你有两个线程(你都在等待完成)它们都在阻塞。
您的第一个线程确实在等待信号量可用。
但是第二个线程当前正在向 STDOUT
写入数据,在您的情况下这也是阻塞的。如果读取 Ruby 进程(例如您的终端)的 STDOUT
的进程不够快,无法读取所有数据,通常会发生这种情况。一旦管道的缓冲区已满,将写入 STDOUT
块,导致线程也不会处于活动状态。
这被 Thread#join
检测到,导致抛出异常。
要解决此问题,您只需确保您从进程的 STDOUT
中读取的速度足够快。然后,我再也无法重现这个问题了。
出于文档目的:我可以通过 运行 ruby ./semaphore.rb | ruby -e "sleep 30"
一致地重现 OP 描述的问题,其中 semaphore.rb
包含问题中显示的代码。