2009-07-02 4 views

Antwort

9

Ich wickelte alle Lösungen, die ich auf dem Weg gefunden (einige andere Probleme wie Benutzer beenden + Piping-Puffer) in ruby parallel gem. Nun ist es so einfach wie:

results = Parallel.map([1,2,3],:in_processes=>4) do |i| 
    execute_something(i) 
end 

oder

results = Parallel.map([1,2,3],:in_threads=>4) do |i| 
    execute_something(i) 
end 
+2

Können Sie hier Objekte oder sogar Klassen als Parameter übergeben? Ich könnte wirklich einen Weg nutzen, um einen Prozess auszuweiten und ihm eine ganze Umgebung zu übergeben. – Automatico

0

Nach der Dokumentation:

Wenn ein Block angegeben wird, dass der Block in der subprocess ausgeführt wird, und die Subprozess endet mit einem Status von Null.

Also, wenn Sie es mit einem Block aufrufen, gibt es 0. Ansonsten funktioniert es im Grunde das gleiche wie das fork() Systemaufruf auf Unix (das Mutter empfängt die PID des neuen Prozesses, das Kind erhält nil).

+0

Wie kann diese Hilfe in das bekommen, was die 'x' zurückgekehrt? – grosser

+0

Der Kindprozess läuft (natürlich) in einem separaten Prozess. Um also den Wert von "x" zu erhalten, müssen Sie wahrscheinlich über Sockets, Pipes oder etwas Ähnliches kommunizieren. Ich bezweifle, dass Sie Variablen innerhalb des Blocks festlegen können, die außerhalb des Blocks reflektiert werden, da der untergeordnete Prozess über separaten Speicher usw. verfügt. – mipadi

0

Die Gabel Kommunikation zwischen zwei Unix-Prozesse ist hauptsächlich der Return-Code und nichts mehr. Sie könnten jedoch einen Dateideskriptor zwischen den beiden Prozessen öffnen und Daten zwischen den Prozessen über diesen Dateideskriptor übergeben: Dies ist der normale Unix-Pipe-Weg.

Wenn Sie die Werte Marshal.dump() und Marshal.load() übergeben würden, könnten Sie problemlos Ruby-Objekte zwischen diesen Ruby-Prozessen übergeben.

0

Sie können Shared Memory dazu verwenden, wenn das Kind nur ein kleiner Ruby-Code sein muss. So etwas wie die folgenden funktionieren:

str = 'from parent' 

Thread.new do 
    str = 'from child' 
end 

sleep(1) 

puts str # outputs "from child" 

Concurrency ziemlich schwierig sein kann, aber, und die gemeinsamen Speicher Zugriff auf diese Art und Weise ist ein großer Teil des Grundes - jedes Mal, wenn Sie eine Variable, und ein anderer Prozess haben könnte es ändern aus unter Ihnen, sollten Sie sehr vorsichtig sein. Alternativ können Sie eine Pipe verwenden, die umständlicher ist, aber wahrscheinlich sicherer für alle außer dem einfachsten Code, und kann auch verwendet werden, um einen beliebigen Befehl auszuführen. Hier ist ein Beispiel, gerade aus dem rdoc für IO.popen:

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

Wir haben eigentlich nur dieses Problem in Rails isolation testing behandeln musste. Ich habe darüber einige on my blog geschrieben.

Im Grunde möchten Sie eine Pipe im Parent und Child öffnen und das Kind in die Pipe schreiben lassen. Hier ist eine einfache Möglichkeit, den Inhalt eines Blocks in einem untergeordneten Prozess und wieder um das Ergebnis zu laufen:

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 

Dann könnten Sie laufen:

do_in_child do 
    require "some_polluting_library" 
    SomePollutingLibrary.some_operation 
end 

Beachten Sie, dass, wenn Sie ein benötigen im Kind , Sie haben im übergeordneten Objekt keinen Zugriff auf diese Bibliothek, sodass Sie ein Objekt dieses Typs mit dieser Methode nicht zurückgeben können. Sie können jedoch jeden Typ zurückgeben, der in beiden verfügbar ist.

Beachten Sie auch, dass viele der Details hier (read.close, Process.wait2(pid)) meist Housekeeping-Details sind, also wenn Sie diese viel verwenden, sollten Sie diese wahrscheinlich in eine Dienstprogrammbibliothek verschieben, die Sie wiederverwenden können.

Beachten Sie, dass dies unter Windows oder JRuby nicht funktioniert, da Forking nicht unterstützt wird.

11

Danke für alle Antworten, habe ich meine Lösung und läuft, müssen noch sehen, wie nicht-Forking Umgebungen zu handhaben, aber es funktioniert für jetzt :)

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 

Sie es in Aktion sehen können @parallel_specs Rails plugin

+0

Haben Sie den Isolationstest-Link gesehen, den ich in meiner Antwort angegeben habe? Es verarbeitet sowohl Forking- als auch Non-Forking-Umgebungen und kann bereits alles tun, was Sie brauchen. –

+0

Ja, lesen Sie auch Ihren Beitrag darüber, krank hinzuzufügen bald! – grosser

1

Ja, Sie einen Subprozess erstellen einen Block innerhalb auszuführen.

Ich empfehle the aw gem:

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

Natürlich ist es verhindert, dass Nebenwirkungen:

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