Sök…


Introduktion

De flesta program körs rad för rad och kör endast en enda process åt gången. Trådar tillåter flera processer att flöda oberoende av varandra. Trådning med flera processorer tillåter program att köra flera processer samtidigt. Det här ämnet dokumenterar implementeringen och användningen av trådar i Python.

Global tolklås

Python multithreading-prestanda kan ofta drabbas på grund av det globala tolklåset . Kort sagt, även om du kan ha flera trådar i ett Python-program, kan bara en bytekodinstruktion exekveras parallellt när som helst, oavsett antalet CPU: er.

Som sådan kan flertrådning i fall där operationer blockeras av externa händelser - som nätverksåtkomst - vara ganska effektiv:

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

Observera att även om varje process tog 2 sekunder att utföra, kunde de fyra processerna tillsammans fungera parallellt, vilket tog totalt 2 sekunder.

Men flertrådning i fall där intensiva beräkningar görs i Python-kod - till exempel mycket beräkning - resulterar inte i mycket förbättringar och kan till och med vara långsammare än att köra parallellt:

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

I det senare fallet kan multiprocessering vara effektiv eftersom flera processer naturligtvis kan utföra flera instruktioner samtidigt:

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

Kör i flera trådar

Använd threading.Thread att köra en funktion i en annan tråd.

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

Kör i flera processer

Använd multiprocessing.Process att köra en funktion i en annan process. Gränssnittet liknar 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

Dela tillstånd mellan trådar

Eftersom alla trådar körs i samma process har alla trådar tillgång till samma data.

Samtidig åtkomst till delad data bör dock skyddas med ett lås för att undvika synkroniseringsproblem.

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}

Dela tillstånd mellan processer

Kod som körs i olika processer delar inte som standard samma data. Men multiprocessing modulen innehåller primitiv för att dela värden över flera processer.

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow