Als ich mit Pythons threading.Event nach einem Beispiel gegoogelt wurde, fand ich eines, das oben missbraucht wurde. Ich bin in einer schlimmen Situation, in der alle drei Beiträge mit Threading.Event in Qiita missbraucht werden, daher erkläre ich, wie man sie richtig verwendet.
Diese Klasse wird verwendet, um einen Thread warten zu lassen, bis ein Ereignis eintritt. Wenn ein Ereignis aus einem anderen Thread generiert wird, wird der wartende Thread fortgesetzt.
Die zwei wichtigsten Methoden sind:
Es gibt auch clear () und is_set (). Weitere Informationen finden Sie unter [Python-Dokumentation] threading.Event.
Lassen Sie uns zunächst sehen, wie Sie es richtig verwenden.
Dies ist das einfachste Beispiel, bei dem nur wait () und set () verwendet werden. Der Thread wartet, bis das Ereignis eintritt, und der Haupt-Thread generiert das Ereignis 3 Sekunden nach dem Start des Threads.
Die erste Hälfte ist etwas lang, da die Zeit und der Threadname so eingestellt sind, dass sie in das Protokoll ausgegeben werden, aber keine Sorge.
from logging import (getLogger, StreamHandler, INFO, Formatter)
#Protokolleinstellungen
handler = StreamHandler()
handler.setLevel(INFO)
handler.setFormatter(Formatter("[%(asctime)s] [%(threadName)s] %(message)s"))
logger = getLogger()
logger.addHandler(handler)
logger.setLevel(INFO)
from threading import (Event, Thread)
import time
event = Event()
def event_example1():
logger.info("Fadenstart")
event.wait()
logger.info("Fadenende")
thread = Thread(target=event_example1)
thread.start()
time.sleep(3)
logger.info("Ereigniseintritt")
event.set()
Ausführungsergebnis
[2016-09-27 00:04:18,400] [Thread-6]Fadenstart
[2016-09-27 00:04:21,406] [MainThread]Ereigniseintritt
[2016-09-27 00:04:21,407] [Thread-6]Fadenende
Wenn Sie sich die Zeit ansehen, können Sie sehen, dass der Thread auf das Eintreten des Ereignisses wartet.
Wenn Sie ein Zeitlimit angeben, wird der Thread nach der angegebenen Anzahl von Sekunden fortgesetzt, auch wenn kein Ereignis auftritt. Der Rückgabewert von wait () ist True, wenn das Ereignis eintritt, andernfalls False.
python
event = Event()
def event_example2():
logger.info("Fadenstart")
while not event.wait(2):
logger.info("Korrekt")
logger.info("Fadenende")
thread = Thread(target=event_example2)
thread.start()
time.sleep(5)
logger.info("Ereigniseintritt")
event.set()
Ausführungsergebnis
[2016-09-27 00:04:21,407] [Thread-7]Fadenstart
[2016-09-27 00:04:23,412] [Thread-7]Korrekt
[2016-09-27 00:04:25,419] [Thread-7]Korrekt
[2016-09-27 00:04:26,409] [MainThread]Ereigniseintritt
[2016-09-27 00:04:26,409] [Thread-7]Fadenende
Sie können sehen, dass das Zeitlimit überschritten wird, auch wenn das Ereignis nicht auftritt. Wenn ein Ereignis auftritt, wird es vor dem Timeout neu gestartet.
Wie in der Dokumentation angegeben, kehrt der Thread nach dem Aufruf von set () zurück, auch wenn Sie wait () aufrufen, ohne zu warten. Wenn Sie Event wiederholt verwenden möchten, müssen Sie clear () aufrufen, um das Ereignis zu löschen.
Das Folgende ist ein Beispiel mit clear ().
Es verwendet auch das Bool-Flag stop
getrennt von event
, um den Thread zu beenden.
python
event = Event()
#Ereignis-Stopp-Flag
stop = False
def event_example3():
logger.info("Fadenstart")
count = 0
while not stop:
event.wait()
event.clear()
count += 1
logger.info(count)
logger.info("Fadenende")
thread = Thread(target=event_example3)
thread.start()
time.sleep(1)
event.set()
time.sleep(1)
event.set()
time.sleep(1)
stop = True
event.set()
thread.join()
Ausführungsergebnis
[2016-09-27 00:04:26,410] [Thread-8]Fadenstart
[2016-09-27 00:04:27,415] [Thread-8] 1
[2016-09-27 00:04:28,417] [Thread-8] 2
[2016-09-27 00:04:29,421] [Thread-8] 3
[2016-09-27 00:04:29,421] [Thread-8]Fadenende
Schauen wir uns als nächstes die falsche Verwendung an, die bei der Suche festgestellt wurde.
So beenden Sie einen Prozess mit mehreren Threads, die sich unendlich wiederholen - Qiita Betreiben Sie den laufenden Thread von außen - Qiita Multithread-Zundokokiyoshi mit Ereignis und Warteschlange - Qiita
Dieses Muster verwendet nur set () und is_set () und verwendet Event nur als Flag. Es mag übertrieben sein zu sagen, dass es falsch ist, aber es macht keinen Sinn, Event zu verwenden, es sei denn, Sie rufen wait () auf.
Beispiel für die Verwendung von Event als Flag
event = Event()
def bad_example1():
logger.info("Fadenstart")
while not event.is_set():
logger.info("Korrekt")
time.sleep(2)
logger.info("Fadenende")
thread = Thread(target=bad_example1)
thread.start()
time.sleep(5)
logger.info("Ereigniseintritt")
event.set()
In solchen Fällen ist es einfacher, die Teile time.sleep () und is_set () im Thread durch Event.wait () zu ersetzen oder bool ohne Event zu verwenden.
wait()verwenden
event = Event()
def bad_example1():
logger.info("Fadenstart")
while not event.wait(timeout=2):
logger.info("Korrekt")
logger.info("Fadenende")
thread = Thread(target=bad_example1)
thread.start()
time.sleep(5)
logger.info("Ereigniseintritt")
event.set()
Verwenden Sie die Bool-Flagge
stop = False
def bad_example1():
logger.info("Fadenstart")
while not stop:
logger.info("Korrekt")
time.sleep(2)
logger.info("Fadenende")
thread = Thread(target=bad_example1)
thread.start()
time.sleep(5)
logger.info("Ereigniseintritt")
stop = True
[Versuchen Sie Python-Threading.Event | CUBE SUGAR STORAGE](http://momijiame.tumblr.com/post/38384408139/python-%E3%81%AE-threadingevent-%E3%82%92%E8% A9% A6% E3% 81% 97% E3% 81% A6% E3% 81% BF% E3% 82% 8B)
Schauen Sie sich zunächst den obigen Beispiellink an.
Wenn in der BlockingQueue-Klasse die Warteschlange beim Aufruf von pop () leer ist, wartet sie, bis ein Ereignis eintritt, und in push () wird beim Hinzufügen eines Werts zur Warteschlange ein Ereignis generiert und der wartende Thread, der pop () aufgerufen hat, neu gestartet.
Consumer ruft wiederholt pop () auf und Producer ruft push () in Intervallen von 1 Sekunde auf, um der Warteschlange zufällige Werte hinzuzufügen.
Wenn Sie sich die Quelle ansehen, wird wait () verwendet, und selbst wenn sie ausgeführt wird, wird in Intervallen von 1 Sekunde ein zufälliger Wert angezeigt, sodass es so aussieht, als würde es das tun, was Sie erwarten.
Ausführungsergebnis
27
88
53
148
:
Wenn Sie jedoch ein Protokoll an die Stelle von wait () setzen, können Sie sehen, dass es sich lächerlich verhält.
def pop(self):
# wait()Schleife, um den verlassenen Thread zurückzugeben
while True:
#Holen Sie sich das Schloss
with self.lock:
if self.queue:
#Gibt alle Elemente in der Warteschlange zurück
return self.queue.pop()
else:
print("waiting...") # <---hinzufügen
#Wenn die Warteschlange leer ist, warten Sie, bis ein anderer Thread ein Element hinzufügt und Sie benachrichtigt
self.event.wait()
Ausführungsergebnis
waiting...
waiting...
waiting...
waiting...
:
Sie werden viel "Warten ..." sehen und Sie können sehen, dass die Bedrohung mit wait () überhaupt nicht wartet.
Dies liegt daran, dass clear () nicht aufgerufen wird. Selbst wenn Sie wait () aufrufen, sobald Sie set () aufrufen, wird es nicht warten.
Damit dies ordnungsgemäß funktioniert, können Sie clear () aufrufen. Es gibt jedoch auch einen Fehler, dass es sich in einem toten Sperrzustand befindet, da es wartet (), während es die Sperre hält Musste repariert werden.
Überarbeitet
def pop(self):
# wait()Schleife, um den verlassenen Thread zurückzugeben
while True:
self.event.clear()
#Holen Sie sich das Schloss
with self.lock:
if self.queue:
#Gibt alle Elemente in der Warteschlange zurück
return self.queue.pop()
print "waiting..."
#Wenn die Warteschlange leer ist, warten Sie, bis ein anderer Thread ein Element hinzufügt und Sie benachrichtigt
self.event.wait()
Ausführungsergebnis
7
waiting...
169
waiting...
113
waiting...
Jetzt wartet es richtig.
Im Python-Standardmodul befindet sich eine [Warteschlange].
Dies ist eine Multithread-Warteschlange, mit der Sie:
--Warten Sie einen Thread, wenn die Warteschlange leer ist, wenn Sie pop () aufrufen.
In der BlockingQueue, die in diesem Beispiel angezeigt wird, wartet beim Aufrufen von "pop ()" auf einen Thread, wenn die Warteschlange leer ist. Dies wurde jedoch bereits mit dem Standardmodul erreicht.
Ich denke, dieses Beispiel wurde nur als Erklärung für das Ereignis geschrieben. Wenn Sie eine Warteschlange mit mehreren Threads verwenden möchten, verwenden Sie das Standardmodul [Warteschlange], anstatt es selbst zu erstellen.
--threading.Event ist ohne wait () bedeutungslos