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



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow