2009-07-02 10 views

Odpowiedz

9

Zawinęłem wszystkie rozwiązania, które znalazłem po drodze (niektóre inne problemy, takie jak wyjście użytkownika + bufory przewodów) do ruby parallel gem. Teraz jest to tak proste, jak:

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

lub

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

Czy można przekazywać obiekty, a nawet klasy, jako parametry tutaj? Naprawdę mógłbym użyć sposobu na rozwidlenie procesu i przekazanie mu całego środowiska. – Automatico

0

Zgodnie z dokumentacją:

Jeśli blok jest określone, że blok jest uruchamiany w podproces oraz podproces kończy się stanem zero.

Więc jeśli nazywają go z bloku, zwraca 0. W przeciwnym razie, działa w zasadzie tak samo jak wywołanie systemowe fork() na Unix (rodzic odbiera PID nowego procesu, dziecko otrzymuje nil).

+0

Jak to może pomóc w osiągnięciu tego, co 'x' wrócił? – grosser

+0

Proces potomny działa w oddzielnym procesie (oczywiście), więc aby uzyskać wartość dowolnego "x", prawdopodobnie będziesz musiał komunikować się przez gniazda, potoki lub coś podobnego. Wątpię, abyś mógł np. Ustawić zmienne wewnątrz bloku, które znajdują odzwierciedlenie poza blokiem, ponieważ proces potomny ma osobną pamięć itd. – mipadi

0

Komunikacja widelca między dwoma procesami uniksowymi to głównie kod powrotu i nic więcej. Można jednak otworzyć plik rejestru między dwoma procesami i przekazać dane między procesami przez ten plik-rejestrator: jest to normalny sposób działania Uniksa.

Jeśli przekażesz wartości Marshal.dump() i Marshal.load(), możesz łatwo przekazać obiekty Ruby pomiędzy tymi procesami Ruby.

0

Możesz użyć pamięci współdzielonej, aby to zrobić, jeśli dziecko musi być małym kawałkiem kodu ruby. Coś jak poniżej będzie działać:

str = 'from parent' 

Thread.new do 
    str = 'from child' 
end 

sleep(1) 

puts str # outputs "from child" 

współbieżności może być dość trudne, chociaż, i dostępu do pamięci współdzielonej w ten sposób jest duża część z tego powodu - za każdym razem masz zmienną i inny proces może go zmienić od ciebie, powinieneś być bardzo ostrożny. Alternatywnie możesz użyć potoku, który jest bardziej uciążliwy, ale prawdopodobnie bezpieczniejszy dla dowolnego, ale najbardziej banalnego kodu, i może być również użyty do uruchomienia dowolnych poleceń. Oto przykład, prosto z rdoc dla IO.popen:

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

My właściwie tylko musiał obsługiwać ten problem w Rails isolation testing. Opublikowalem o tym około on my blog.

Zasadniczo, co chcesz zrobić, to otworzyć potok w rodzicu i podrzędnym, i niech dziecko napisze do fajki. Oto prosty sposób uruchomić zawartość bloku w procesie potomnym i wrócić Rezultat:

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 

Następnie można uruchomić:

do_in_child do 
    require "some_polluting_library" 
    SomePollutingLibrary.some_operation 
end 

Zauważ, że jeśli zrobić wymagają u dziecka , nie będziesz mieć dostępu do tej biblioteki w obiekcie nadrzędnym, więc nie możesz zwrócić obiektu tego typu za pomocą tej metody. Możesz jednak zwrócić dowolny typ, który jest dostępny w obu.

Należy również pamiętać, że wiele szczegółów tutaj (read.close, Process.wait2(pid)) to głównie szczegóły dotyczące sprzątania, więc jeśli korzystasz z tego dużo, prawdopodobnie powinieneś przenieść to do biblioteki narzędziowej, którą możesz ponownie wykorzystać.

Na koniec zwróć uwagę, że to nie zadziała w systemie Windows lub JRuby, ponieważ nie obsługuje rozwidlania.

11

Dzięki za wszystkie odpowiedzi, mam moje rozwiązanie działa, nadal trzeba zobaczyć, jak radzić sobie środowisk spoza rozwidlone, ale na razie to działa :)

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 

można zobaczyć go w akcji @parallel_specs Rails plugin

+0

Czy widziałeś w moim odpowiedzi link testowy izolacji? Obsługuje zarówno środowiska rozwidlające się, jak i nie-rozwidlające i może już robić wszystko, czego potrzebujesz. –

+0

Tak, przeczytaj także swój wpis na ten temat, źle dodaj to wkrótce! – grosser

1

Tak, można utworzyć podproces aby wykonać blok wewnątrz.

Polecam the aw gem:

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

oczywiście, zapobiega skutki uboczne:

arr = ['foo'] 
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"] 
arr # => ["foo"] 
Powiązane problemy