Heim >Backend-Entwicklung >Python-Tutorial >Threads in Python verstehen
Wir werden einige Beispiele für die Verwendung von Threads in Python sehen und wie man Thread-Konkurrenz vermeidet.
Sie sollten das folgende Beispiel mehrmals ausführen, damit Sie erkennen, dass Threads unvorhersehbar sind und bei jeder Ausführung unterschiedliche Ergebnisse liefern. Haftungsausschluss: Vergessen Sie von nun an, was Sie über die GIL gehört haben, denn die GIL hat keinen Einfluss auf das, was ich zu zeigen versuche.
Wir werden fünf verschiedene URLs anfordern:
Einzelner Thread
import time import urllib2 def get_responses(): urls = [ 'http://www.google.com', 'http://www.amazon.com', 'http://www.ebay.com', 'http://www.alibaba.com', 'http://www.reddit.com' ] start = time.time() for url in urls: print url resp = urllib2.urlopen(url) print resp.getcode() print "Elapsed time: %s" % (time.time()-start) get_responses()
Die Ausgabe lautet:
http://www.google.com 200 http://www.amazon.com 200 http://www.ebay.com 200 http://www.alibaba.com 200 http://www.reddit.com 200 Elapsed time: 3.0814409256
Erklärung:
Die URL-Reihenfolge wird angefordert
Sofern die CPU keine Antwort von einer URL erhält, fordert sie nicht die nächste URL an
Netzwerkanfragen dauern lange, sodass die CPU im Leerlauf bleibt und auf die Rückkehr der Netzwerkanfrage wartet.
Multithreading
import urllib2 import time from threading import Thread class GetUrlThread(Thread): def __init__(self, url): self.url = url super(GetUrlThread, self).__init__() def run(self): resp = urllib2.urlopen(self.url) print self.url, resp.getcode() def get_responses(): urls = [ 'http://www.google.com', 'http://www.amazon.com', 'http://www.ebay.com', 'http://www.alibaba.com', 'http://www.reddit.com' ] start = time.time() threads = [] for url in urls: t = GetUrlThread(url) threads.append(t) t.start() for t in threads: t.join() print "Elapsed time: %s" % (time.time()-start) get_responses()
Ausgabe:
http://www.reddit.com 200 http://www.google.com 200 http://www.amazon.com 200 http://www.alibaba.com 200 http://www.ebay.com 200 Elapsed time: 0.689890861511
Erklärung:
Erkennte die Verbesserung der Programmausführungszeit
Wir haben ein Multithread-Programm geschrieben, um die Wartezeit der CPU zu verkürzen. Wenn wir auf die Rückkehr einer Netzwerkanforderung in einem Thread warten, kann die CPU zu anderen Threads wechseln, um Netzwerkanforderungen in anderen Threads zu stellen.
Wir erwarten, dass ein Thread eine URL verarbeitet, daher übergeben wir beim Instanziieren der Thread-Klasse eine URL.
Beim Thread-Running wird die run()
-Methode in der Klasse ausgeführt.
Wir möchten jedoch, dass jeder Thread run()
ausführt.
Erstellen Sie für jede URL einen Thread und rufen Sie die Methode start()
auf, die die CPU anweist, die Methode run()
im Thread auszuführen.
Wir möchten die aufgewendete Zeit berechnen, wenn die Ausführung aller Threads abgeschlossen ist, also rufen wir die Methode join()
auf.
join()
Sie können den Hauptthread anweisen, auf das Ende dieses Threads zu warten, bevor er die nächste Anweisung ausführt.
Wir rufen die Methode join()
für jeden Thread auf, sodass wir die Laufzeit berechnen, nachdem alle Threads die Ausführung abgeschlossen haben.
Über den Thread:
Die CPU führt die Methode start()
möglicherweise nicht unmittelbar nach dem Aufruf von run()
aus.
Sie können die Ausführungsreihenfolge von run()
zwischen verschiedenen Threads nicht bestimmen.
Für einen einzelnen Thread wird garantiert, dass die Anweisungen in der run()
-Methode der Reihe nach ausgeführt werden.
Dies liegt daran, dass zuerst die URL innerhalb des Threads angefordert und dann das zurückgegebene Ergebnis gedruckt wird.
Wir werden ein Programm verwenden, um den Ressourcenwettbewerb zwischen mehreren Threads zu demonstrieren und dieses Problem zu beheben.
from threading import Thread #define a global variable some_var = 0 class IncrementThread(Thread): def run(self): #we want to read a global variable #and then increment it global some_var read_value = some_var print "some_var in %s is %d" % (self.name, read_value) some_var = read_value + 1 print "some_var in %s after increment is %d" % (self.name, some_var) def use_increment_thread(): threads = [] for i in range(50): t = IncrementThread() threads.append(t) t.start() for t in threads: t.join() print "After 50 modifications, some_var should have become 50" print "After 50 modifications, some_var is %d" % (some_var,) use_increment_thread()
Führen Sie dieses Programm mehrmals aus und Sie werden unterschiedliche Ergebnisse sehen.
Erklärung:
Es gibt eine globale Variable, die alle Threads ändern möchten.
Alle Threads sollten 1 zu dieser globalen Variablen hinzufügen.
Bei 50 Threads sollte der Endwert 50 betragen, was aber nicht der Fall ist.
Warum hat es nicht die 50 erreicht?
Wenn some_var
15
ist, liest Thread t1
some_var
, und in diesem Moment übergibt die CPU die Kontrolle an einen anderen Thread t2
.
t2
Der some_var
, den der Thread liest, ist auch 15
Sowohl t1
als auch t2
fügen some_var
zu 16
hinzu
Was wir damals erwartet hatten, war, dass aus t1
t2
zwei Threads some_var + 2
zu 17
ist. 50
from threading import Lock, Thread lock = Lock() some_var = 0 class IncrementThread(Thread): def run(self): #we want to read a global variable #and then increment it global some_var lock.acquire() read_value = some_var print "some_var in %s is %d" % (self.name, read_value) some_var = read_value + 1 print "some_var in %s after increment is %d" % (self.name, some_var) lock.release() def use_increment_thread(): threads = [] for i in range(50): t = IncrementThread() threads.append(t) t.start() for t in threads: t.join() print "After 50 modifications, some_var should have become 50" print "After 50 modifications, some_var is %d" % (some_var,) use_increment_thread()Die erneute Ausführung dieses Programms erzielte die erwarteten Ergebnisse. Erklärung:
die Sperre erhält, bevor er einige Vorgänge ausführt. Andere Threads führen nicht den gleichen Vorgang t1
t1
aus, bevor
die Sperre aufhebt
我们想要确定的是一旦线程t1
已经读取了some_var
,直到t1
完成了修改some_var
,其他的线程才可以读取some_var
这样读取和修改some_var
成了逻辑上的原子操作。
让我们用一个例子来证明一个线程不能影响其他线程内的变量(非全局变量)。
time.sleep()可以使一个线程挂起,强制线程切换发生。
from threading import Thread import time class CreateListThread(Thread): def run(self): self.entries = [] for i in range(10): time.sleep(1) self.entries.append(i) print self.entries def use_create_list_thread(): for i in range(3): t = CreateListThread() t.start() use_create_list_thread()
运行几次后发现并没有打印出争取的结果。当一个线程正在打印的时候,cpu切换到了另一个线程,所以产生了不正确的结果。我们需要确保print self.entries
是个逻辑上的原子操作,以防打印时被其他线程打断。
我们使用了Lock(),来看下边的例子。
from threading import Thread, Lock import time lock = Lock() class CreateListThread(Thread): def run(self): self.entries = [] for i in range(10): time.sleep(1) self.entries.append(i) lock.acquire() print self.entries lock.release() def use_create_list_thread(): for i in range(3): t = CreateListThread() t.start() use_create_list_thread()
这次我们看到了正确的结果。证明了一个线程不可以修改其他线程内部的变量(非全局变量)。
原文出处: Akshar Raaj
Das obige ist der detaillierte Inhalt vonThreads in Python verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!