2012-01-19 20 views
5

Sto lavorando in multi threading in ruby. Il frammento di codice èDeadlock in Ruby join()

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} 

non sto usando alcun blocco mutex. Ma ho stallo .. In seguito è l'uscita del frammento di codice di cui sopra ..

s 2s 6s 1s 8s 11s 10s 7s 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: deadlock rilevato

l'uscita sopra ci dice che deadlock è dopo l'array user_ids è nullo e succede con join() e stop() della classe Thread in ruby ​​.. Che cosa sta succedendo e qual'è la soluzione a questo errore ??

+0

La mia risposta è stata utile? Hai risolto il problema? –

risposta

19

La simples codice per riprodurre questo problema è:

t = Thread.new { Thread.stop } 
t.join # => exception in `join': deadlock detected (fatal) 

Discussione :: interrompere → nil

arresta l'esecuzione del thread corrente, messa in un “sonno” Stato e pianifica l'esecuzione di un altro thread.

discussione # uniscono → thr
discussione # uniscono (limite) → thr

il thread chiamante sarà sospendere l'esecuzione e correre thr. Non restituisce fino alla chiusura delle tratte o finché non sono passati i secondi limite. Se il limite di tempo scade, verrà restituito nil, altrimenti viene restituito thr.

Per quanto ho capito si chiamano Thread.join senza indicazione sul filetto e attendere che per uscire, ma il filo figlio chiama Thread.stop e va in sleep stato. Questa è una situazione deadloc - thread principale attende che il thread figlio esca, ma il thread figlio sta dormendo e non risponde.

Se si chiama join con limit parametro allora filo bambino sarà interrotta dopo il timeout senza causare stallo al vostro programma:

t = Thread.new { Thread.stop } 
t.join 1 # => Process finished with exit code 0 

Suggerirei per uscire il tuo thread di lavoro dopo che hanno fatto il lavoro con Thread.exit o ottenere liberarsi del ciclo infinito e raggiungere la fine del thread di esecuzione normalmente, ad esempio:

if user_id == nil 
    raise StopIteration 
end 

#or 
if user_id == nil 
    Thread.exit 
end 
+0

Nice write-up; molto bene. – Phrogz

+0

fantastico, grazie – glebm

0

Se ricevo le vostre intenzioni destra vorrei prendere in considerazione qualcosa di più semplice (e probabilmente più sicuro, users_ids.pop() dall'interno filo sembra spaventoso per me):

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) 
5

Oltre alla risposta di Alex Kliuchnikau, io aggiungo che #join potrebbe sollevare questo errore quando thread è in attesa per Queue#pop. Una soluzione semplice e consapevole è chiamare #join con un timeout.

Questo è da rubino 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:[email protected](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 
+0

e se la coda è una connessione persistente http? come live stream e il risultato nel flusso è casuale, il t.join (5) funziona ancora? –

+0

@crazy_phage, non ho avuto questo caso d'uso ma non vedo perché non dovrebbe funzionare. Nel caso di connessione HTTP persistente, suppongo che stai implementando un timeout dopo il quale la connessione dovrebbe essere chiusa, corretta? Se vuoi aspettare per sempre, puoi impostare un valore molto alto, come 10 anni. – akostadinov

+0

beh, è ​​come se la connessione http fosse una pipe e ho un altro thread da leggere dalla pipe, se uso t.join not j.join 5, si bloccherà, ma non ho visto perché sta succedendo, perchè l'ho eseguito in sidekiq, il log non ha mostrato nulla. Quindi, ho appena visto la tua risposta, e suppongo che questo sia il motivo per cui ciò accade. –