Python Language
Processus et threads
Recherche…
Introduction
La plupart des programmes sont exécutés ligne par ligne, exécutant un seul processus à la fois. Les threads permettent à plusieurs processus de circuler indépendamment les uns des autres. Le threading avec plusieurs processeurs permet aux programmes d'exécuter plusieurs processus simultanément. Cette rubrique documente l'implémentation et l'utilisation des threads en Python.
Global Interpreter Lock
Les performances du multithreading Python peuvent souvent être affectées par le Global Interpreter Lock . En bref, même si vous pouvez avoir plusieurs threads dans un programme Python, une seule instruction de bytecode peut s'exécuter en parallèle à tout moment, quel que soit le nombre de processeurs.
En tant que tel, le multithreading dans les cas où les opérations sont bloquées par des événements externes - comme l’accès au réseau - peut être très efficace:
import threading
import time
def process():
time.sleep(2)
start = time.time()
process()
print("One run took %.2fs" % (time.time() - start))
start = time.time()
threads = [threading.Thread(target=process) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print("Four runs took %.2fs" % (time.time() - start))
# Out: One run took 2.00s
# Out: Four runs took 2.00s
Notez que même si chaque process
pris 2 secondes pour s’exécuter, les quatre processus ensemble ont pu être exécutés efficacement en parallèle, en prenant 2 secondes au total.
Cependant, le multithreading dans les cas où des calculs intensifs sont effectués en code Python - comme beaucoup de calculs - ne se traduit pas par une amélioration considérable et peut même être plus lent que l'exécution en parallèle:
import threading
import time
def somefunc(i):
return i * i
def otherfunc(m, i):
return m + i
def process():
for j in range(100):
result = 0
for i in range(100000):
result = otherfunc(result, somefunc(i))
start = time.time()
process()
print("One run took %.2fs" % (time.time() - start))
start = time.time()
threads = [threading.Thread(target=process) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print("Four runs took %.2fs" % (time.time() - start))
# Out: One run took 2.05s
# Out: Four runs took 14.42s
Dans ce dernier cas, le multitraitement peut être efficace car plusieurs processus peuvent, bien entendu, exécuter plusieurs instructions simultanément:
import multiprocessing
import time
def somefunc(i):
return i * i
def otherfunc(m, i):
return m + i
def process():
for j in range(100):
result = 0
for i in range(100000):
result = otherfunc(result, somefunc(i))
start = time.time()
process()
print("One run took %.2fs" % (time.time() - start))
start = time.time()
processes = [multiprocessing.Process(target=process) for _ in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
print("Four runs took %.2fs" % (time.time() - start))
# Out: One run took 2.07s
# Out: Four runs took 2.30s
Exécution dans plusieurs threads
Utilisez threading.Thread
pour exécuter une fonction dans un autre thread.
import threading
import os
def process():
print("Pid is %s, thread id is %s" % (os.getpid(), threading.current_thread().name))
threads = [threading.Thread(target=process) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
# Out: Pid is 11240, thread id is Thread-1
# Out: Pid is 11240, thread id is Thread-2
# Out: Pid is 11240, thread id is Thread-3
# Out: Pid is 11240, thread id is Thread-4
Exécution dans plusieurs processus
Utilisez multiprocessing.Process
pour exécuter une fonction dans un autre processus. L'interface est similaire à threading.Thread
:
import multiprocessing
import os
def process():
print("Pid is %s" % (os.getpid(),))
processes = [multiprocessing.Process(target=process) for _ in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
# Out: Pid is 11206
# Out: Pid is 11207
# Out: Pid is 11208
# Out: Pid is 11209
État de partage entre les threads
Comme tous les threads s'exécutent dans le même processus, tous les threads ont accès aux mêmes données.
Toutefois, l'accès simultané aux données partagées doit être protégé par un verrou pour éviter les problèmes de synchronisation.
import threading
obj = {}
obj_lock = threading.Lock()
def objify(key, val):
print("Obj has %d values" % len(obj))
with obj_lock:
obj[key] = val
print("Obj now has %d values" % len(obj))
ts = [threading.Thread(target=objify, args=(str(n), n)) for n in range(4)]
for t in ts:
t.start()
for t in ts:
t.join()
print("Obj final result:")
import pprint; pprint.pprint(obj)
# Out: Obj has 0 values
# Out: Obj has 0 values
# Out: Obj now has 1 values
# Out: Obj now has 2 valuesObj has 2 values
# Out: Obj now has 3 values
# Out:
# Out: Obj has 3 values
# Out: Obj now has 4 values
# Out: Obj final result:
# Out: {'0': 0, '1': 1, '2': 2, '3': 3}
État de partage entre les processus
Le code exécuté dans différents processus ne partage pas, par défaut, les mêmes données. Cependant, le module de multiprocessing
contient des primitives permettant de partager des valeurs entre plusieurs processus.
import multiprocessing
plain_num = 0
shared_num = multiprocessing.Value('d', 0)
lock = multiprocessing.Lock()
def increment():
global plain_num
with lock:
# ordinary variable modifications are not visible across processes
plain_num += 1
# multiprocessing.Value modifications are
shared_num.value += 1
ps = [multiprocessing.Process(target=increment) for n in range(4)]
for p in ps:
p.start()
for p in ps:
p.join()
print("plain_num is %d, shared_num is %d" % (plain_num, shared_num.value))
# Out: plain_num is 0, shared_num is 4