Ik probeer threading in Python te begrijpen. Ik'heb de documentatie en voorbeelden bekeken, maar eerlijk gezegd zijn veel voorbeelden te geavanceerd en ik'heb moeite om ze te begrijpen.
Hoe laat je duidelijk zien dat taken worden verdeeld voor multi-threading?
Hier's een eenvoudig voorbeeld: u moet een paar alternatieve URL's proberen en de inhoud van de eerste die reageert terugzenden.
import Queue
import threading
import urllib2
# called by each thread
def get_url(q, url):
q.put(urllib2.urlopen(url).read())
theurls = ["http://google.com", "http://yahoo.com"]
q = Queue.Queue()
for u in theurls:
t = threading.Thread(target=get_url, args = (q,u))
t.daemon = True
t.start()
s = q.get()
print s
Dit is een geval waar threading wordt gebruikt als een eenvoudige optimalisatie: elke subthread wacht op een URL om op te lossen en te antwoorden, om de inhoud op de wachtrij te zetten; elke thread is een daemon (zal'het proces niet in stand houden als de hoofdthread eindigt -- dat'komt vaker voor dan niet); de hoofdthread start alle subthreads, doet een get
op de wachtrij om te wachten tot een van hen een put
heeft gedaan, geeft dan de resultaten weg en beëindigt (wat alle subthreads die misschien nog draaien neerhaalt, aangezien ze'daemon-threads zijn).
Correct gebruik van threads in Python is steevast verbonden met I/O operaties (aangezien CPython toch al geen gebruik maakt van meerdere cores om CPU-gebonden taken uit te voeren, is de enige reden voor threading het niet blokkeren van het proces terwijl er'gewacht wordt op wat I/O). Wachtrijen zijn bijna altijd de beste manier om werk aan threads uit te besteden en/of de resultaten van het werk te verzamelen, en ze'zijn intrinsiek threadsafe dus ze besparen je zorgen over sloten, voorwaarden, gebeurtenissen, semaforen en andere inter-thread coördinatie/communicatie concepten.
NOTE: Voor daadwerkelijke parallellisatie in Python moet je de multiprocessing module gebruiken om meerdere processen te forken die parallel worden uitgevoerd (vanwege de globale interpreter lock bieden Python threads interleaving maar worden in feite serieel uitgevoerd, niet parallel, en zijn alleen nuttig bij het interleaving van I/O operaties).
Echter, als je alleen op zoek bent naar interleaving (of I/O operaties uitvoert die parallel kunnen worden uitgevoerd ondanks de global interpreter lock), dan is de threading module de plaats om te beginnen. Als een heel eenvoudig voorbeeld, laten we het probleem bekijken van het sommeren van een groot bereik door deelbereiken parallel te sommeren:
import threading
class SummingThread(threading.Thread):
def __init__(self,low,high):
super(SummingThread, self).__init__()
self.low=low
self.high=high
self.total=0
def run(self):
for i in range(self.low,self.high):
self.total+=i
thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join() # This waits until the thread has completed
thread2.join()
# At this point, both threads have completed
result = thread1.total + thread2.total
print result
Merk op dat het bovenstaande een heel dom voorbeeld is, omdat het absoluut geen I/O doet en serieel zal worden uitgevoerd, zij het interleaved (met de toegevoegde overhead van context switching) in CPython als gevolg van de globale interpreter lock.
Zoals anderen al aangaven, kan CPython alleen threads gebruiken voor I.O.-wachttijden als gevolg van GIL. Als je wilt profiteren van meerdere cores voor CPU-gebonden taken, gebruik dan multiprocessing:
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()