Ruby 报错: chaser.rb:35 in `join': No live threads left. Deadlock

jablie · 2014年02月28日 · 最后由 bryan 回复于 2015年06月10日 · 3871 次阅读

在看《Programming Ruby》第二版。 运行书中的例子:

class Chaser
    attr_reader :count

    def initialize(name)
        @name = name
        @count = 0
    end

    def chase(other)
        while @count  < 5
            while @count - other.count > 1
                Thread.pass
            end

            @count += 1
            print "#@name  : #{count}\n"
        end
    end
end


c1 = Chaser.new("A")
c2 = Chaser.new("B")

threads = [
    Thread.new { Thread.stop ; c1.chase(c2) },
    Thread.new { Thread.stop ; c2.chase(c1) }
]

start_index  = rand(2)

threads[start_index].run
threads[1-start_index].run
threads.each { |t| t.join }

运行时报这样的错:

chaser.rb:35:in `join': No live threads left. Deadlock? (fatal)
        from chaser.rb:35:in `block in <main>'
        from chaser.rb:35:in `each'
        from chaser.rb:35:in `<main>'

Ruby 版本:ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]

Ruby 新手,不知道如何处理

如下加个 sleep 0.1 试试。可能是主线程跑太快了,或子线程从 Thread.stop 恢复需要时间调度

sleep 0.1 threads[start_index].run threads[1-start_index].run

#1 楼 @bryan 确实,加了 sleep 之后解决了。谢谢。

#1 楼 @bryan 你好,我也遇到这样的问题,照你说的解决了。但是有一个问题,为什么 sleep 0.1 会是在 run 之前加呢?而且我试了一下,在 run 之后加是不行的。照理说,应该是在调用 run 之后再等待线程调度吧?求解!我是在 windows 下的

@hugohe 你好。两个子线程跑起来先执行 Thread.stop,让自身进入 sleep 模式。主线程的 run 代码就是为了唤醒他们。导致死锁的原因应该是调用 run 的时候,子线程还没有跑起来(new 以后自动运行)。等 run 之后,子线程自动跑起来,又把自己 stop 了,而 join 没有 timeout 参数,导致死锁。所以加 sleep 一定要在 run 前面,确保 run 的执行发生在 stop 之后。 如果不想用 hardcode 的时间,可以加个 countdown 变量,虽然理论上还是无法保证 100% 不死锁,但发生的概率就非常小了。参考代码如下:

....
wait_thread = 2

threads = [
Thread.new {
          wait_thread -= 1 Thread.stop ;  c1.chase(c2)
      },
Thread.new {
          wait_thread -= 1 Thread.stop ; c2.chase(c1)
      }
]

while wait_thread > 0 
end

start_index  = rand(2)
....

这里有类似的问题讨论 http://stackoverflow.com/questions/8925001/deadlock-in-ruby-join 以及 Thread 相关 API 的说明 http://rubylearning.com/satishtalim/ruby_threads.html

需要 登录 后方可回复, 如果你还没有账号请 注册新账号