Restituzione di dati da processi biforcuta
Domanda
Se faccio
Process.fork do
x
end
come posso sapere che cosa X restituito (ad esempio true / fase / string)?
(scrittura di un file / database non è un'opzione ...)
Soluzione 3
ho avvolto tutte le soluzioni ho trovato lungo la strada (alcuni altri problemi come l'uscita dell'utente + tubazioni-buffer) in rubino parallelamente gemma. Ora è facile come:
results = Parallel.map([1,2,3],:in_processes=>4) do |i|
execute_something(i)
end
o
results = Parallel.map([1,2,3],:in_threads=>4) do |i|
execute_something(i)
end
Altri suggerimenti
In realtà abbiamo avuto solo per gestire questo problema in Rails isolamento test . Ho pubblicato su di esso un po ' sul mio blog .
In sostanza, ciò che si vuole fare è aprire un tubo nel genitore e figlio, e avere il bambino scrivere al tubo. Ecco un modo semplice per eseguire il contenuto di un blocco in un processo figlio e tornare il risultato:
def do_in_child
read, write = IO.pipe
pid = fork do
read.close
result = yield
Marshal.dump(result, write)
exit!(0) # skips exit handlers.
end
write.close
result = read.read
Process.wait(pid)
raise "child failed" if result.empty?
Marshal.load(result)
end
Poi si potrebbe eseguire:
do_in_child do
require "some_polluting_library"
SomePollutingLibrary.some_operation
end
Si noti che se si fa un richiedono nel bambino, non si avrà accesso a tale libreria nel genitore, quindi non è possibile restituire un oggetto di quel tipo con questo metodo. Tuttavia, si potrebbe restituire qualsiasi tipo che è disponibile sia.
Si noti inoltre che molti dei dettagli qui (read.close
, Process.wait2(pid)
) sono per lo più i dettagli di pulizia, quindi se si utilizza questo molto probabilmente si dovrebbe spostare questo fuori in una libreria di utilità che è possibile riutilizzare.
Infine, ricordiamo che questo non funzionerà su Windows o JRuby, dal momento che non supportano fork.
Grazie per tutte le risposte, ho ottenuto la mia soluzione attivo e funzionante, hanno ancora bisogno di vedere come gestire ambienti non si biforcano, ma per ora funziona:)
read, write = IO.pipe
Process.fork do
write.puts "test"
end
Process.fork do
write.puts 'test 2'
end
Process.wait
Process.wait
write.close
puts read.read
read.close
è possibile vederlo in azione @ parallel_specs Rails plug
Sì, è possibile creare un sottoprocesso per eseguire un blocco all'interno.
Aw.fork! { 6 * 7 } # => 42
Naturalmente, impedisce di effetti collaterali:
arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]
Secondo la documentazione:
Se viene specificato un blocco, tale blocco viene eseguito nel sottoprocesso, e il sottoprocesso termina con uno stato di zero.
Quindi, se lo si chiama con un blocco, restituisce 0. In caso contrario, funziona fondamentalmente lo stesso come la chiamata di sistema fork()
su Unix (il genitore riceve il PID del nuovo processo, il bambino riceve nil
) .
La comunicazione tra due processi forcella Unix è principalmente il codice di ritorno e nulla più. Tuttavia, si potrebbe aprire un filedescriptor tra i due processi e passaggio dei dati tra i processi più di questo filedescriptor:. Questo è il modo normale tubo di Unix
Se volete passare Marshal.dump () e Marshal.load () i valori, si potrebbe facilmente passare oggetti di Ruby tra quei processi Ruby.
È possibile utilizzare la memoria condivisa per fare questo se il bambino ha solo bisogno di essere un piccolo pezzo di codice Ruby. Qualcosa di simile a quanto segue funzionerà:
str = 'from parent'
Thread.new do
str = 'from child'
end
sleep(1)
puts str # outputs "from child"
La concorrenza può essere piuttosto difficile, però, e l'accesso a memoria condivisa in questo modo è una grande parte della ragione - ogni volta che hai una variabile e un altro processo potrebbe cambiare da sotto, si dovrebbe essere molto cauti. In alternativa, è possibile utilizzare un tubo, che è più ingombrante, ma probabilmente più sicuro per qualsiasi ma il codice più banale, e può anche essere utilizzato per eseguire qualsiasi comando arbitrario. Ecco un esempio, non appena estratto dalla rdoc per IO.popen:
f = IO.popen("uname")
p f.readlines # outputs "Darwin", at least on my box :-)