2010-10-25 18 views
6

Stavo esaminando le capacità di elaborazione parallela/asincrona di Ruby e leggevo molti articoli e post di blog. Ho guardato attraverso EventMachine, Fibre, Revactor, Reia, ecc, ecc Purtroppo, non ero in grado di trovare una soluzione semplice, efficace (e non-IO-blocco) per questo molto semplice caso d'uso:Concorrenza di ruby ​​/ elaborazione asincrona (con caso d'uso semplice)

File.open('somelogfile.txt') do |file| 
    while line = file.gets  # (R) Read from IO 
    line = process_line(line) # (P) Process the line 
    write_to_db(line)   # (W) Write the output to some IO (DB or file) 
    end 
end 

è che si può vedere, il mio piccolo script sta eseguendo tre operazioni lette (R), processo (P) & scrittura (W). Supponiamo - per semplicità - che ogni operazione richiede esattamente 1 unità di tempo (ad esempio 10 ms), il codice attuale sarebbe quindi fare qualcosa di simile (5 righe):

Time:  123456789(15 units in total) 
Operations: RPWRPWRPWRPWRPW 

Ma, mi piacerebbe fare qualcosa in questo modo:

Time:  1234567 (7 units in total) 
Operations: RRRRR 
      PPPPP 
       WWWWW 

Ovviamente, ho potuto eseguire tre processi (lettore, processore & scrittore) e trasmettere leggere linee dal lettore nella coda del processore e quindi passare linee trasformate nella coda scrittore (tutti coordinati ad esempio tramite RabbitMQ) . Ma il caso d'uso è così semplice, non sembra giusto.

Eventuali indizi su come ciò potrebbe essere fatto (senza passare da Ruby a Erlang, Closure o Scala)?

+1

Le scritture devono essere chiamate nello stesso ordine in cui sono state lette? –

+0

No, questo è il punto in cui possono essere completamente asincroni. – Dim

risposta

1

Check out pesca (http://peach.rubyforge.org/). Fare un parallelo "ciascuno" non potrebbe essere più semplice. Tuttavia, come dice la documentazione, è necessario eseguire JRuby per utilizzare il threading nativo della JVM.

Vedere la risposta di Jorg Mittag a this SO question per un sacco di dettagli sulle capacità di multithreading dei vari interpreti Ruby.

+0

Hmm, la pesca non è proprio quello che sto cercando. Non voglio eseguire l'RPW in parallelo, voglio staccare l'attività 3 l'una dall'altra ed eseguirle in modo asincrono. La risposta di Jorg Mittag offre un'ottima introduzione. Sono ben consapevole delle opzioni offerte, ma nessuna sembra avere una risposta al mio problema. – Dim

3

Se è necessario che sia veramente parallelo (da un singolo processo), credo che dovrai utilizzare JRuby per ottenere veri thread nativi e senza GIL.

È possibile utilizzare qualcosa come DRb per distribuire l'elaborazione su più processi/core, ma per il proprio caso di utilizzo questo è un po 'troppo. Invece, si potrebbe provare di avere più processi comunicano utilizzando tubi:

$ cat somelogfile.txt | ruby ./proc-process | ruby ./proc-store 

In questo scenario ogni pezzo è il suo proprio processo che può essere eseguito in parallelo, ma sono comunicare utilizzando STDIN/STDOUT. Questo è probabilmente l'approccio più semplice (e più veloce) al tuo problema.

# proc-process 
while line = $stdin.gets do 
    # do cpu intensive stuff here 
    $stdout.puts "data to be stored in DB" 
    $stdout.flush # this is important 
end 

# proc-store 
while line = $stdin.gets do 
    write_to_db(line) 
end 
+1

Ho pensato che GIL di Ruby 1.9 ti permette di fare roba CPU in un thread mentre un altro thread fa I/O - cioè, proibisce solo due thread che fanno roba CPU. –

+0

Stai parlando di Fibre?La mia comprensione limitata di Fibre è che invece di thread che hanno ciascuno una quantità di tempo CPU condivisa, il codice distribuisce esplicitamente l'elaborazione alla fibra, che può gestire l'operazione di blocco dell'IO e tornare immediatamente al codice chiamante. Ciò riduce la quantità di tempo che si spende in attesa, ma non penso che ti consentirà di estendere più di una CPU per processo. Penso che GIL significhi che solo un thread di esecuzione può essere eseguito in qualsiasi momento. http://www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/ – JEH

+2

L'uso di pipe è una buona soluzione per suddividere il problema in 3 processi separati, ma non è asincrono. Si tratta infatti di una soluzione "Ruby", quindi abbastanza difficile da implementare nell'ambito di un'applicazione più ampia. Il "problema" che ho delineato sopra è un semplice esempio di elaborazione guidata da IO. Sto cercando di capire di cosa Ruby è capace in quest'area e cosa potrebbe mancare. – Dim