Heim >Backend-Entwicklung >Python-Tutorial >Threads in Python verstehen

Threads in Python verstehen

巴扎黑
巴扎黑Original
2017-04-30 14:38:281249Durchsuche

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.

Beispiel 1

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.

Beispiel 2

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.


  • t2Der 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


  • Hier herrscht Konkurrenz um Ressourcen.


  • Die gleiche Situation kann auch in anderen Threads auftreten, sodass das Endergebnis möglicherweise kleiner als

    ist. 50

Ressourcenwettbewerb lösen

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 wird verwendet, um Rennbedingungen zu verhindern


  • Wenn Thread

    die Sperre erhält, bevor er einige Vorgänge ausführt. Andere Threads führen nicht den gleichen Vorgang t1t1

  • aus, bevor
    die Sperre aufhebt

  • 我们想要确定的是一旦线程t1已经读取了some_var,直到t1完成了修改some_var,其他的线程才可以读取some_var


  • 这样读取和修改some_var成了逻辑上的原子操作。

  实例3

  让我们用一个例子来证明一个线程不能影响其他线程内的变量(非全局变量)。

  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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn