2017-09-07 1 views
1

J'ai un test simple où j'exécute une boucle d'événements Python asyncio en utilisant la méthode run_forever, puis je l'arrête immédiatement dans un autre thread. Cependant, la boucle d'événement ne semble pas se terminer. J'ai le cas de test suivant:Python asyncio: la boucle d'événements ne semble pas s'arrêter lorsque la méthode stop est appelée

import asyncio 
from threading import Thread 

loop = asyncio.get_event_loop() 
thread = Thread(target=loop.run_forever) 
thread.start() 
print('Started!') 
loop.stop() 
print('Requested stop!') 
thread.join() 
print('Finished!') 

Ce cas test se imprime:

Started! 
Requested stop! 

Ainsi, le test semble bloquer le thread.join(), en attendant que la boucle d'événement pour terminer.

Si je vide mes fils, je reçois le texte suivant:

Thread 0x00007000087ec000 (most recent call first): 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 577 in select 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 1388 in _run_once 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 421 in run_forever 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 862 in run 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 914 in _bootstrap_inner 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 882 in _bootstrap 

Current thread 0x00007fffc6b273c0 (most recent call first): 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 1070 in _wait_for_tstate_lock 
    File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 1054 in join 

Je ne l'ai pas regardé à profondément le code Python, mais selectors.py semble attendre pour le travail. Je suppose qu'il est possible que ce problème se produise parce que j'ai appelé stop alors qu'il n'y a plus de travail pour la boucle d'événement, mais cela semble être une limitation assez problématique. Ou peut-être que j'ai mal compris quelque chose sur la façon dont cela devrait fonctionner?

+1

Pourquoi utilisez-vous 'asyncio' en conjonction avec un' thread'? – stovfl

+0

@stovfl - cet exemple nécessite un thread séparé. Puisque les blocs 'run_forever', la seule façon d'arrêter la boucle est de le faire dans un thread séparé. – Neil

Répondre

3

Documentation says au sujet de classe boucle d'événement:

Cette classe est pas sûre.

Et further:

Une boucle d'événement se déroule dans un thread et exécute toutes les callbacks et tâches dans le même fil. [...] Pour planifier un rappel à partir d'un thread différent, la méthode AbstractEventLoop.call_soon_threadsafe() doit être utilisée. Exemple:

loop.call_soon_threadsafe(callback, *args) 

semble être ce que nous avons besoin:

import asyncio 
from threading import Thread 

loop = asyncio.get_event_loop() 
thread = Thread(target=loop.run_forever) 
thread.start() 
print('Started!') 
loop.call_soon_threadsafe(loop.stop) # here 
print('Requested stop!') 
thread.join() 
print('Finished!') 

Prints:

Started! 
Requested stop! 
Finished! 
+0

C'est génial, merci. L'API asyncio est un peu inintéressant par endroits! Vous ne pouvez pas appeler 'stop' à partir du même thread que la boucle d'événement en cours d'exécution, mais cela ne fonctionne pas lorsque vous appelez depuis un thread différent, sans utiliser' call_soon_threadsafe'. – Neil

+0

Oui, vous pouvez certainement appeler 'stop' à partir du même thread que la boucle d'événement en cours d'exécution - à partir d'une tâche qui est exécutée sur cette boucle d'événements. –