Lorsque j'ai été recherché sur Google un échantillon qui utilise threading.Event de Python, j'en ai trouvé un qui était mal utilisé en haut. Je suis dans une situation désastreuse où les trois messages utilisant le threading.Event dans Qiita sont mal utilisés, alors je vais vous expliquer comment les utiliser correctement.
Cette classe est utilisée pour faire attendre un thread jusqu'à ce qu'un événement se produise, et lorsqu'un événement est généré à partir d'un autre thread, le thread en attente reprend.
Les deux méthodes les plus importantes sont:
Il existe également clear () et is_set (). Voir [documentation Python] threading.Event pour plus d'informations.
Voyons d'abord comment l'utiliser correctement.
C'est l'exemple le plus simple utilisant uniquement wait () et set (). Le thread attend jusqu'à ce que l'événement se produise et le thread principal génère l'événement 3 secondes après le démarrage du thread.
La première moitié est un peu longue car l'heure et le nom du thread sont définis pour être affichés dans le journal, mais ne vous inquiétez pas.
from logging import (getLogger, StreamHandler, INFO, Formatter)
#Paramètres du journal
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("Début du fil")
event.wait()
logger.info("Fin de fil")
thread = Thread(target=event_example1)
thread.start()
time.sleep(3)
logger.info("Occurrence d'événement")
event.set()
Résultat d'exécution
[2016-09-27 00:04:18,400] [Thread-6]Début du fil
[2016-09-27 00:04:21,406] [MainThread]Occurrence d'événement
[2016-09-27 00:04:21,407] [Thread-6]Fin de fil
Si vous regardez l'heure, vous pouvez voir que le thread attend que l'événement se produise.
Si vous spécifiez un délai, le thread reprendra après le nombre de secondes spécifié, même si aucun événement ne se produit. La valeur de retour de wait () est True lorsque l'événement se produit, False sinon.
python
event = Event()
def event_example2():
logger.info("Début du fil")
while not event.wait(2):
logger.info("C'est vrai")
logger.info("Fin de fil")
thread = Thread(target=event_example2)
thread.start()
time.sleep(5)
logger.info("Occurrence d'événement")
event.set()
Résultat d'exécution
[2016-09-27 00:04:21,407] [Thread-7]Début du fil
[2016-09-27 00:04:23,412] [Thread-7]C'est vrai
[2016-09-27 00:04:25,419] [Thread-7]C'est vrai
[2016-09-27 00:04:26,409] [MainThread]Occurrence d'événement
[2016-09-27 00:04:26,409] [Thread-7]Fin de fil
Vous pouvez voir qu'il expire même si l'événement ne se produit pas. En outre, lorsqu'un événement se produit, il redémarre avant l'expiration du délai.
Comme indiqué dans la documentation, une fois que vous appelez set (), même si vous appelez wait (), le thread reviendra sans attendre. Par conséquent, si vous souhaitez utiliser Event à plusieurs reprises, vous devez appeler clear () pour effacer l'événement.
Voici un exemple utilisant clear ().
Il utilise également l'indicateur booléen stop
séparément de ʻevent` pour terminer le thread.
python
event = Event()
#Indicateur d'arrêt d'événement
stop = False
def event_example3():
logger.info("Début du fil")
count = 0
while not stop:
event.wait()
event.clear()
count += 1
logger.info(count)
logger.info("Fin de fil")
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()
Résultat d'exécution
[2016-09-27 00:04:26,410] [Thread-8]Début du fil
[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]Fin de fil
Ensuite, regardons la mauvaise utilisation qui est ressortie de la recherche.
Comment terminer un processus avec plusieurs threads qui bouclent indéfiniment --Qiita Faites fonctionner le thread en cours d'exécution de l'extérieur --Qiita Zundokokiyoshi multithread utilisant Event and Queue --Qiita
Ce modèle utilise uniquement set () et is_set () et n'utilise Event que comme indicateur. Il peut être exagéré de dire que c'est faux, mais il ne sert à rien d'utiliser Event à moins d'appeler wait ().
Exemple d'utilisation de l'événement comme simple indicateur
event = Event()
def bad_example1():
logger.info("Début du fil")
while not event.is_set():
logger.info("C'est vrai")
time.sleep(2)
logger.info("Fin de fil")
thread = Thread(target=bad_example1)
thread.start()
time.sleep(5)
logger.info("Occurrence d'événement")
event.set()
Dans ce cas, il est plus simple de remplacer les parties time.sleep () et is_set () dans le thread par Event.wait (), ou d'utiliser bool sans Event.
wait()utilisation
event = Event()
def bad_example1():
logger.info("Début du fil")
while not event.wait(timeout=2):
logger.info("C'est vrai")
logger.info("Fin de fil")
thread = Thread(target=bad_example1)
thread.start()
time.sleep(5)
logger.info("Occurrence d'événement")
event.set()
Utilisez l'indicateur booléen
stop = False
def bad_example1():
logger.info("Début du fil")
while not stop:
logger.info("C'est vrai")
time.sleep(2)
logger.info("Fin de fil")
thread = Thread(target=bad_example1)
thread.start()
time.sleep(5)
logger.info("Occurrence d'événement")
stop = True
[Essayez 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)
Tout d'abord, jetez un œil à l'exemple de lien ci-dessus.
Dans la classe BlockingQueue, si la file d'attente est vide lorsque pop () est appelée, elle attend qu'un événement se produise, et dans push (), lorsqu'une valeur est ajoutée à la file d'attente, un événement est généré et le thread en attente qui a appelé pop () est redémarré.
Le consommateur appelle à plusieurs reprises pop () et le producteur appelle push () à intervalles d'une seconde pour ajouter des valeurs aléatoires à la file d'attente.
En regardant la source, il utilise wait (), et même s'il est exécuté, une valeur aléatoire est affichée à 1 seconde d'intervalle, il semble donc qu'il fait ce que vous attendez.
Résultat d'exécution
27
88
53
148
:
Cependant, si vous mettez un journal à la place de wait (), vous pouvez voir qu'il se comporte de manière ridicule.
def pop(self):
# wait()Boucle pour retourner le thread qui s'est terminé
while True:
#Obtenez la serrure
with self.lock:
if self.queue:
#Renvoie tous les éléments de la file d'attente
return self.queue.pop()
else:
print("waiting...") # <---ajouter à
#Si la file d'attente est vide, attendez qu'un autre thread ajoute un élément et vous avertisse
self.event.wait()
Résultat d'exécution
waiting...
waiting...
waiting...
waiting...
:
Vous verrez beaucoup de "attente ..." et vous pouvez voir que la menace n'attend pas du tout avec wait ().
C'est parce que clear () n'est pas appelé, donc même si vous appelez wait () une fois que vous appelez set (), il n'attendra pas.
Pour que cela fonctionne correctement, vous pouvez appeler clear (), mais il y a aussi un bogue selon lequel il sera dans un état de verrouillage mort car il attend () tout en maintenant le verrou, donc c'est comme suit Besoin d'être réparé.
modifié
def pop(self):
# wait()Boucle pour retourner le thread qui s'est terminé
while True:
self.event.clear()
#Obtenez la serrure
with self.lock:
if self.queue:
#Renvoie tous les éléments de la file d'attente
return self.queue.pop()
print "waiting..."
#Si la file d'attente est vide, attendez qu'un autre thread ajoute un élément et vous avertisse
self.event.wait()
Résultat d'exécution
7
waiting...
169
waiting...
113
waiting...
Maintenant, il attend correctement.
Il y a une [file d'attente] dans le module standard Python.
Il s'agit d'une file d'attente multithread qui vous permet de:
--Attendez un thread si la file d'attente est vide lorsque vous appelez pop () --Si la file d'attente est pleine lorsque vous appelez push (), attendez qu'elle soit libre
Dans le BlockingQueue qui apparaît dans cet exemple, lorsque pop () est appelé, si la file d'attente est vide, elle attend un thread
, mais cela a déjà été réalisé avec le module standard.
Je pense que cet exemple est juste écrit comme une explication de l'événement. Si vous souhaitez utiliser une file d'attente avec plusieurs threads, utilisez le module standard queue au lieu de le créer vous-même.
--threading.Event n'a pas de sens sans utiliser wait () --Utilisez bool si vous souhaitez utiliser des indicateurs