2010-06-30 8 views
10

я использую BrB разделять источник данных для различных рабочих процессов в Ruby 1.9, что я вилку с процессом # вилкой, как следующее:Трассировка затора в Руби

Thread.abort_on_exception = true 

fork do 
    puts "Initializing data source process... (PID: #{Process.pid})" 
    data = DataSource.new(files) 

    BrB::Service.start_service(:object => data, :verbose => false, :host => host, :port => port) 
    EM.reactor_thread.join 
end

Рабочие раздвоенным следующим образом:

8.times do |t| 
    fork do 
    data = BrB::Tunnel.create(nil, "brb://#{host}:#{port}", :verbose => false) 

    puts "Launching #{threads_num} worker threads... (PID: #{Process.pid})"  

    threads = [] 
    threads_num.times { |i| 
     threads << Thread.new { 
     while true 
      begin 
      worker = Worker.new(data, config) 

      rescue OutOfTargetsError 
      break 

      rescue Exception => e 
      puts "An unexpected exception was caught: #{e.class} => #{e}" 
      sleep 5 

      end 
     end 
     } 
    } 
    threads.each { |t| t.join } 

    data.stop_service 
    EM.stop 
    end 
end

Это работает в значительной степени прекрасно, но примерно через 10 минут бега я получаю следующее сообщение об ошибке:

bootstrap.rb:47:in `join': deadlock detected (fatal) 
     from bootstrap.rb:47:in `block in ' 
     from bootstrap.rb:39:in `fork' 
     from bootstrap.rb:39:in `'

Теперь эта ошибка не говорит мне о том, где тупик на самом деле происходит, он указывает только на соединение в потоке EventMachine.

Как проследить, в какой момент программа блокируется?

+0

Вы пробовали положить 'Thread.exit' до конца блока? – glebm

ответ

5

Он блокируется при объединении в родительский поток, эта информация точна. Чтобы проследить, где он блокируется в дочернем потоке, попробуйте обернуть работу потока в timeout block. Вам нужно будет временно удалить спасение для всех исключений таймаута для рейза.

В настоящее время родительский поток пытается присоединиться ко всем потокам в порядке, блокируя до тех пор, пока он не будет завершен. Однако каждый поток будет присоединяться только к OutOfTargetsError. Тупика можно избежать, используя недолговечные потоки и перемещая цикл while в родительский. Нет гарантий, но может быть, что-то вроде этого?

8.times do |t| 
    fork do 
    running = true 
    Signal.trap("INT") do 
     puts "Interrupt signal received, waiting for threads to finish..." 
     running = false 
    end 

    data = BrB::Tunnel.create(nil, "brb://#{host}:#{port}", :verbose => false) 

    puts "Launching max #{threads_num} worker threads... (PID: #{Process.pid})"  

    threads = [] 
    while running 
     # Start new threads until we have threads_num running 
     until threads.length >= threads_num do 
     threads << Thread.new { 
      begin 
      worker = Worker.new(data, config) 
      rescue OutOfTargetsError 
      rescue Exception => e 
      puts "An unexpected exception was caught: #{e.class} => #{e}" 
      sleep 5 
      end 
     } 
     end 

     # Make sure the parent process doesn't spin too much 
     sleep 1 

     # Join finished threads 
     finished_threads = threads.reject &:status 
     threads -= finished_threads 
     finished_threads.each &:join 
    end 

    data.stop_service 
    EM.stop 
    end 
end 
+0

Эй, чувак, какая-нибудь удача в этом подходе? – captainpete

2

У меня была такая же проблема, как и в случае с этим фрагментом кода.

# Wait for all threads (other than the current thread and 
# main thread) to stop running. 
# Assumes that no new threads are started while waiting 
def join_all 
    main  = Thread.main  # The main thread 
    current = Thread.current # The current thread 
    all  = Thread.list  # All threads still running 
    # Now call join on each thread 
    all.each{|t| t.join unless t == current or t == main } 
end 

Источник: The Ruby Programming Language, O'Reilly (2008)

Смежные вопросы