> Modules standards > Autres modules > multi-processing
multi-processing
On peut utiliser un simple fork (ne marche pas sous windows) :
- os.fork() renvoie le PID de l'enfant chez le parent et 0 chez l'enfant.
- il ne faut pas oublier de faire un waitpid sur le process :
- os.waitpid(myPid, 0) : pour attendre un process fils particulier.
- os.waitpid(-1, 0) : pour attendre n'importe quel process fils.
- os.waitpid(-1, os.WNOHANG) : pour collecter n'importe quel process fils, mais sans bloquer.
- une exception OSError est retournée si aucun process fils à attendre.
la valeur retournée est alors une paire (pid, status) avec le pid du process terminé (0 si aucun) et le statut d'erreur (0 si aucune)
- Exemple :
for i in range(3):
pid = os.fork()
if pid != 0:
# I am the parent
pids.add(pid)
else:
# I am the child
...
sys.exit(0)
while True:
# in the parent
print('waiting for children')
try:
(pid, status) = os.waitpid(-1, os.WNOHANG)
except OSError:
break
Package multiprocessing : permet de faire la même chose que fork, mais portable (marche sous windows) et plus évolué :
- principe est de démarrer de définir des process, les démarrer, et faire un join dessus quand ils ont fini :
from multiprocessing import Process
p = Process(target = myFunc, args = (i, ))
p.start()
p.join()
- p.join(0) : ne bloque pas si le process n'est pas encore fini (mais il faudra recommencer). L'argument est en fait le timeout en secondes.
- une fois qu'on a fait le join, faire if p.is_alive() pour savoir si le process est toujours vivant
- p.exitcode : le statut de retour quand le process est fini (0 si ok).
- on peut échanger des objets entre les sous-process et le process avec une queue (mais attention, il faut que ces données soient sérialisables) :
from multiprocessing import Process, Queue
def myFunc(q):
d = {'a': 1, 'b': 3}
q.put(d)
l = [4, 6, 8]
q.put(l)
q = Queue()
p = Process(target = myFunc, args = (q,))
p.start()
while True:
try:
# Le False évite de bloquer
result = queue.get(False)
except: Exception:
# si rien à lire dans la queue, exception déclenchée.
pass
# join, sans bloquer
p.join(0)
if not p.is_alive():
# Le process est terminé
print(p.exitcode)
print(result)
break
time.sleep(1)
attention : si on ne lit pas la queue, le processus fils ne peut pas se terminer, et il va y avoir un deadlock.
attention : si on met des objets trop gros dans la queue, elle peut trop se remplir et le put() bloque, ce qui bloque le process fils.
- pipe pour la communication entre les process :
from multiprocessing import Process, Pipe
def myFunc(conn):
conn.send('some text')
print('in child', conn.recv())
(parentConn, childConn) = Pipe()
p = Process(target = myFunc, args = (childConn,))
p.start()
print('in parent', parentConn.recv())
parentConn.send('other text')
p.join()
Multithreading versus multiprocessing :
- quand on fait du multi-threading en python, 2 threads ne peuvent pas exécuter du code python en même temps à cause d'un Global Interpreter Lock ! Le code ne sera accéléré que si beaucoup d'IO, si portions appelées font appel à du code extérieur ou à du code C (comme les applications avec numpy). Donc du calcul pure en python ne va pas plus vite avec des threads !!!
- quand on fait du multi-processing, c'est plus lourd, car il faut créer des process, et la communication entre process est plus difficile car ils ne partagent pas la mémoire.
Multithreading :
- Exemple :
import threading
def runIt(x):
...
threads = []
for i in [1, 2]:
thread = threading.Thread(target = runIt, args = ('thread' + str(i), ))
threads.append(thread)
thread.start()
for thread in threads:
thread.join() # Attend que le thread soit fini
- on peut donner un timeout à join, qui peut être 0 si on ne veut pas attendre : thread.join(0)
- thread.isAlive() : teste si le thread vit encore.
- la fonction appelée semble voir la même chose qu'une fonction quelconque (par exemple, variables globales sont vues).
Copyright python-simple.com
programmer en python, tutoriel python, graphes en python, Aymeric Duclert