Python Language
Processer och trådar
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