Как отследить тупик в Ruby - PullRequest
10 голосов
/ 01 июля 2010

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

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 минут работы я получаю следующую ошибку:

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

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

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

Ответы [ 2 ]

5 голосов
/ 30 апреля 2011

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

В настоящее время родительский поток пытается объединить все потоки по порядку, блокируя до тех пор, пока не завершится.Однако каждый поток будет присоединяться только на 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
3 голосов
/ 13 июля 2012

У меня была та же проблема, и я решил ее, используя следующий фрагмент кода:

# 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

Источник: Язык программирования Ruby, O'Reilly (2008)

...