Ricerca…


introduzione

La maggior parte dei programmi viene eseguita riga per riga, eseguendo solo un singolo processo alla volta. I thread consentono a più processi di fluire indipendentemente l'uno dall'altro. Il threading con più processori consente ai programmi di eseguire più processi contemporaneamente. Questo argomento documenta l'implementazione e l'uso dei thread in Python.

Global Interpreter Lock

Le prestazioni di multithreading di Python possono spesso soffrire a causa del Global Interpreter Lock . In breve, anche se è possibile avere più thread in un programma Python, solo un'istruzione bytecode può essere eseguita in parallelo in qualsiasi momento, indipendentemente dal numero di CPU.

Di conseguenza, il multithreading nei casi in cui le operazioni sono bloccate da eventi esterni, come l'accesso alla rete, può essere piuttosto 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

Si noti che anche se ogni process impiegava 2 secondi per essere eseguito, i quattro processi insieme erano in grado di funzionare in modo efficace in parallelo, prendendo 2 secondi in totale.

Tuttavia, il multithreading nei casi in cui i calcoli intensivi vengono eseguiti in codice Python, come un sacco di calcoli, non comporta molti miglioramenti e può anche essere più lento rispetto all'esecuzione in parallelo:

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

In quest'ultimo caso, il multiprocessing può essere efficace in quanto più processi possono, ovviamente, eseguire più istruzioni contemporaneamente:

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

Esecuzione in più thread

Usa threading.Thread per eseguire una funzione in un altro 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

Esecuzione in più processi

Usa multiprocessing.Process per eseguire una funzione in un altro processo. L'interfaccia è simile alla 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

Condivisione dello stato tra thread

Poiché tutti i thread sono in esecuzione nello stesso processo, tutti i thread hanno accesso agli stessi dati.

Tuttavia, l'accesso concorrente ai dati condivisi deve essere protetto con un blocco per evitare problemi di sincronizzazione.

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}

Condivisione dello stato tra processi

Il codice in esecuzione in processi diversi non condivide, per impostazione predefinita, gli stessi dati. Tuttavia, il modulo multiprocessing contiene le primitive per aiutare a condividere i valori tra più processi.

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow