Czy można uruchomić funkcję w podprocesie bez tworzenia wątków lub pisania oddzielnego pliku / skryptu?

83
import subprocess

def my_function(x):
    return x + 100

output = subprocess.Popen(my_function, 1) #I would like to pass the function object and its arguments
print output 
#desired output: 101

Znalazłem tylko dokumentację dotyczącą otwierania podprocesów za pomocą oddzielnych skryptów. Czy ktoś wie, jak przekazywać obiekty funkcyjne, a nawet prosty sposób na przekazywanie kodu funkcji?

wroscoe
źródło
1
Uważam, że szukasz modułu wieloprocesorowego .
Noctis Skytower

Odpowiedzi:

112

Myślę, że szukasz czegoś bardziej podobnego do modułu wieloprocesorowego:

http://docs.python.org/library/multiprocessing.html#the-process-class

Moduł podprocesu służy do tworzenia procesów i robienia rzeczy na ich wejściu / wyjściu - nie do uruchamiania funkcji.

Oto multiprocessingwersja twojego kodu:

from multiprocessing import Process, Queue

# must be a global function    
def my_function(q, x):
    q.put(x + 100)

if __name__ == '__main__':
    queue = Queue()
    p = Process(target=my_function, args=(queue, 1))
    p.start()
    p.join() # this blocks until the process terminates
    result = queue.get()
    print result
Brian McKenna
źródło
20
Możesz użyć processifydekoratora jako skrótu: gist.github.com/2311116
schlamar
3
Zakładam, że to klonuje interpreter Pythona i całe jego środowisko dla podprocesu?
Jens
1
Oto rozwidlenie processify, które działa w Pythonie 3 i obsługuje funkcje generatora. gist.github.com/stuaxo/889db016e51264581b50
Stuart Axon
4
Zauważ, że ten kod zawiera zakleszczenie na wypadek, gdybyś przekazywał przez kolejkę nietrywialne duże dane - zawsze queue.get () przed dołączeniem do procesu, w przeciwnym razie zawiesi się przy próbie zapisu do kolejki, podczas gdy nic jej nie odczytuje.
Petr Baudis
@schlamar Chcę uruchomić funkcję w tle, ale mam pewne ograniczenia zasobów i nie mogę uruchamiać funkcji tyle razy, ile chcę i chcę ustawić w kolejce dodatkowe wykonania funkcji. Czy masz pomysł, jak mam to zrobić? Mam pytanie tutaj . Czy mógłbyś spojrzeć na moje pytanie? Każda pomoc byłaby świetna!
Amir
17

Możesz użyć standardowego forkwywołania systemowego Unix , jak os.fork(). fork()utworzy nowy proces z tym samym uruchomionym skryptem. W nowym procesie zwróci 0, podczas gdy w starym procesie zwróci identyfikator nowego procesu.

child_pid = os.fork()
if child_pid == 0:
  print "New proc"
else:
  print "Old proc"

W przypadku biblioteki wyższego poziomu, która zapewnia obsługę wieloprocesorową, która zapewnia przenośną abstrakcję do korzystania z wielu procesów, istnieje moduł wieloprocesorowy . Jest artykuł na temat IBM DeveloperWorks, Multiprocessing with Python , z krótkim wprowadzeniem do obu technik.

Brian Campbell
źródło
Jestem ciekawy; dlaczego głos przeciw? Czy jest coś nie tak w mojej odpowiedzi?
Brian Campbell,
Wieloprocesorowość to nie tylko wyższy poziom wrappera wokół fork (), to wieloplatformowy zestaw narzędzi do wieloprocesorowości (który używa fork na unixie). Co jest ważne, ponieważ oznacza to, że działa na, powiedzmy, Windows, podczas gdy fork () nie. Edycja: I to był powód negatywnego głosu, chociaż później zdecydowałem, że prawdopodobnie nie było tego warte. Jednak za późno, żeby to cofnąć. Edit2: Lub raczej, powodem było sugerowanie fork (), gdy nie jest to platforma wieloplatformowa.
Devin Jeanpierre,
3
@Devin, jeśli chcesz, zawsze możesz cofnąć swój głos przeciw.
Alex Martelli,
W takim razie zredagowano, by to wyjaśnić. Wyraźnie wspomniałem, że forknie jest przenośny; Generalnie podam nieprzenośne odpowiedzi wraz z informacją, że są one nieprzenośne i pozwolę pytającemu zdecydować, czy to wystarczy. Kiedy zredagowałem moją odpowiedź, powinieneś być w stanie usunąć głos przeciw, jeśli uważasz, że wystarczająco go poprawiłem; choć bez urazy, jeśli nie, chciałem tylko sprawdzić, co zrobiłem źle.
Brian Campbell,
@Alex, nie, nie możesz. Po upływie określonego czasu nie możesz go cofnąć, dopóki nie nastąpi edycja. Upłynęło tyle czasu, zanim ponownie przemyślałem, stąd komentarz „za późno”. W każdym razie, jak powiedziałem, zdecydowałem, że nie warto, więc zniknęło. Doceniam też i rozumiem twoje powody, i cieszę się, że i tak nie byłoby urazów. : p
Devin Jeanpierre
5

Powyższy post Briana McKenny o przetwarzaniu wieloprocesowym jest naprawdę pomocny, ale jeśli chcesz przejść ścieżką wątkową (w przeciwieństwie do opartej na procesach), ten przykład pomoże Ci zacząć:

import threading
import time

def blocker():
    while True:
        print "Oh, sorry, am I in the way?"
        time.sleep(1)

t = threading.Thread(name='child procs', target=blocker)
t.start()

# Prove that we passed through the blocking call
print "No, that's okay" 

Możesz również użyć tej setDaemon(True)funkcji, aby natychmiast uruchomić wątek w tle.

Alexis Benoist
źródło