2010-06-30 5 views
10

Io uso BrB di condividere un DataSource per vari processi di lavoro in Ruby 1.9 che ho forcella con Processo # forchetta come il seguente:Tracciare una situazione di stallo in Ruby

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

I lavoratori sono biforcuta come segue:

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

Questo funziona più o meno perfetta, ma dopo circa 10 minuti di corsa ricevo il seguente errore:

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 `'

Ora questo errore non mi dice molto su dove si sta effettivamente verificando il deadlock, ma mi indirizza solo al join sul thread EventMachine.

Come rintracciare a quale punto il programma si blocca?

+0

Hai provato mettendo 'Thread.exit' prima della fine del blocco? – glebm

risposta

5

Si sta bloccando su join nel thread principale, quella informazione è accurata. Per tracciare il punto in cui si blocca nel thread secondario, provare a completare il lavoro del thread in un timeout block. Dovrai rimuovere temporaneamente il recupero catch-all per l'eccezione di timeout da aumentare.

Attualmente il thread padre tenta di unire tutti i thread in ordine, bloccandoli fino a quando non è terminato. Tuttavia, ogni thread si unirà a un OutOfTargetsError. Il deadlock potrebbe essere evitato usando thread di breve durata e spostando il ciclo while nel genitore. Nessuna garanzia, ma forse qualcosa di simile?

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

Ehi amico, fortuna con questo approccio? – captainpete

2

Ho avuto lo stesso problema di averlo provato utilizzando questo snippet di codice.

# 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 

Fonte: The Ruby Programming Language, O'Reilly (2008)