2009-07-02 9 views

Respuesta

9

Envolví en ruby parallel gem todas las soluciones que encontré en el camino (algunos otros problemas como el usuario que sale + búferes de tuberías). Ahora es tan fácil como:

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 
+2

¿Puede pasar objetos, o incluso clases, como parámetros aquí? Realmente podría usar una forma de bifurcar un proceso y pasarle un entorno completo. – Automatico

0

De acuerdo con la documentación:

Si se especifica un bloque, bloque que se ejecuta en el subproceso y el el subproceso termina con un estado de cero.

Así que si usted lo llama con un bloque, devuelve 0. De lo contrario, funciona básicamente igual que la llamada fork() sistema en Unix (el padre recibe el PID del nuevo proceso, el niño recibe nil).

+0

¿Cómo puede esta ayuda para conseguir lo que la 'x' regresado? – grosser

+0

El proceso hijo se ejecuta en un proceso separado (obviamente), por lo que para obtener el valor de cualquier "x", probablemente tendrá que comunicarse a través de sockets, pipes o algo similar. Dudo que pueda, por ejemplo, establecer variables dentro del bloque que se reflejan fuera del bloque, ya que el proceso secundario tiene memoria separada, etc. – mipadi

0

La comunicación de la horquilla entre dos procesos Unix es principalmente el código de retorno y nada más. Sin embargo, podría abrir un descriptor de archivo entre los dos procesos y pasar datos entre los procesos a través de este descriptor de archivo: esta es la forma normal de conexión de Unix.

Si pasa los valores de Marshal.dump() y Marshal.load(), puede pasar fácilmente objetos de Ruby entre esos procesos de Ruby.

0

Puede usar la memoria compartida para hacer esto si el niño solo necesita una pequeña porción de código ruby. Algo así como el siguiente trabajo:

str = 'from parent' 

Thread.new do 
    str = 'from child' 
end 

sleep(1) 

puts str # outputs "from child" 

concurrencia puede ser bastante complicado, sin embargo, y el acceso a la memoria compartida de esta manera es una gran parte de la razón - en cualquier momento que tenga una variable y otro proceso puede cambiarlo fuera de ti, deberías ser muy cauteloso. Alternativamente, puede usar una tubería, que es más engorrosa pero probablemente más segura para cualquier código que no sea el más trivial, y también se puede usar para ejecutar cualquier comando arbitrario. He aquí un ejemplo, directamente de la rdoc para IO.popen:

f = IO.popen("uname") 
p f.readlines  # outputs "Darwin", at least on my box :-) 
30

En realidad sólo tenía que manejar este problema en Rails isolation testing. Publiqué sobre eso algunos on my blog.

Básicamente, lo que quiere hacer es abrir una tubería en el padre y el hijo, y hacer que el niño escriba en la tubería. Aquí está una manera simple de ejecutar el contenido de un bloque en un proceso hijo y volver el resultado:

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 

Posteriormente, se podría ejecutar:

do_in_child do 
    require "some_polluting_library" 
    SomePollutingLibrary.some_operation 
end 

Tenga en cuenta que si lo hace un requieren en el niño , no tendrá acceso a esa biblioteca en el padre, por lo que no puede devolver un objeto de ese tipo con este método. Sin embargo, puede devolver cualquier tipo que esté disponible en ambos.

También tenga en cuenta que muchos de los detalles aquí (read.close, Process.wait2(pid)) son en su mayoría detalles de la economía doméstica, por lo que si utiliza esto mucho probablemente debería moverlo a una biblioteca de utilidad que puede volver a utilizar.

Finalmente, tenga en cuenta que esto no funcionará en Windows o JRuby, ya que no son compatibles con el bifurcación.

11

Gracias por todas las respuestas, tengo mi solución en funcionamiento, todavía necesitan ver cómo manejar entornos no se bifurcan, pero por ahora funciona :)

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 

se puede ver en acción @parallel_specs Rails plugin

+0

¿Ha visto el enlace de la prueba de aislamiento que proporcioné en mi respuesta? Maneja entornos de bifurcación y no bifurcación, y puede que ya haga todo lo que necesita. –

+0

¡Sí, también leí su publicación al respecto, añadiré esto pronto! – grosser

1

sí, se puede crear un subproceso para ejecutar un bloque interior.

recomiendo the aw gem:

Aw.fork! { 6 * 7 } # => 42 

Por supuesto, se impide efectos secundarios:

arr = ['foo'] 
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"] 
arr # => ["foo"] 
Cuestiones relacionadas