我正在使用ruby中的多线程。 代码段是
threads_array = Array.new(num_of_threads)
1.upto(num_of_threads) do |i|
Thread.abort_on_exception = true
threads_array[i-1] = Thread.new {
catch(:exit) do
print "s #{i}"
user_id = nil
loop do
user_id = user_ids.pop()
if user_id == nil
print "a #{i}"
Thread.stop()
end
dosomething(user_id)
end
end
}
end
#puts "after thread"
threads_array.each {|thread| thread.join}
我没有使用任何互斥锁。但我得到了死锁..以下是上面代码片段的输出..
s 2s 6s 8s 1s 11s 7s 10s 14s 16s 21s 24s 5s 26s 3s 19s 20s 23s 4s 28s 9s 12s 18s 22s 29s 30s 27s 13s 17s 15s 25a 4a 10a 3a 6a 21a 24a 16a 9a 18a 5a 28a 20a 2a 22a 11a 29a 8a 14a 23a 26a 1a 19a 7a 12fatal:检测到死锁
上面的输出告诉我们,死锁是在user_ids数组为null之后,并且在ruby中发生了Thread类的join()和stop()。实际发生了什么,这个错误的解决方案是什么?
答案 0 :(得分:24)
重现此问题的简单代码是:
t = Thread.new { Thread.stop }
t.join # => exception in `join': deadlock detected (fatal)
Thread :: stop →nil
停止执行当前线程,将其置于“睡眠”状态 state,并安排另一个线程的执行。
线程#join →thr
线程#joed(限制)→thr调用线程将暂停执行并运行thr。不归 直到thr退出或直到限制秒数过去。如果时间限制 到期,将返回nil,否则返回thr。
据我所知,你在线程上没有参数调用Thread.join
并等待它退出,但子线程调用Thread.stop并进入sleep
状态。这是一个deadloc情况 - 主线程等待子线程退出,但子线程正在休眠而没有响应。
如果使用join
参数调用limit
,则子线程将在超时后中止,而不会导致程序死锁:
t = Thread.new { Thread.stop }
t.join 1 # => Process finished with exit code 0
我建议在使用Thread.exit
完成工作后退出工作线程,或者摆脱无限循环并正常到达执行结束线程,例如:
if user_id == nil
raise StopIteration
end
#or
if user_id == nil
Thread.exit
end
答案 1 :(得分:7)
除了Alex Kliuchnikau的回答之外,我还要补充一点,#join
可能会在线程等待Queue#pop
时引发此错误。一个简单而有意识的解决方案是调用#join
并超时。
这是来自ruby 2.2.2:
[27] pry(main)> q=Queue.new
=> #<Thread::Queue:0x00000003a39848>
[30] pry(main)> q << "asdggg"
=> #<Thread::Queue:0x00000003a39848>
[31] pry(main)> q << "as"
=> #<Thread::Queue:0x00000003a39848>
[32] pry(main)> t = Thread.new {
[32] pry(main)* while s = q.pop
[32] pry(main)* puts s
[32] pry(main)* end
[32] pry(main)* }
asdggg
as
=> #<Thread:0x00000003817ce0@(pry):34 sleep>
[33] pry(main)> q << "asg"
asg
=> #<Thread::Queue:0x00000003a39848>
[34] pry(main)> q << "ashg"
ashg
=> #<Thread::Queue:0x00000003a39848>
[35] pry(main)> t.join
fatal: No live threads left. Deadlock?
from (pry):41:in `join'
[36] pry(main)> t.join(5)
=> nil
答案 2 :(得分:1)
如果我的意图是正确的,我会考虑更简单的东西(可能更安全,users_ids.pop()
从线程中看起来对我来说很吓人):
user_ids = (0..19).to_a
number_of_threads = 3
user_ids \
.each_slice(user_ids.length / number_of_threads + 1) \
.map { |slice|
Thread.new(slice) { |s|
puts s.inspect
}
}.map(&:join)