web-dev-qa-db-fra.com

RuntimeError: cette boucle d'événement est déjà en cours d'exécution dans python

Je pense que je reçois cette erreur car mon code appelle asyncio.get_event_loop().run_until_complete(foo()) deux fois. Une fois à partir de foo() et une deuxième fois à partir d'une fonction appelée par foo(). Ma question est alors: pourquoi cela devrait-il être un problème? Pourquoi devrais-je même veiller à ce que cette boucle soit en cours d'exécution?


Je pense qu’une modification a été apportée à cette question, qui l’a obscurcie (certaines personnes préfèrent suivre les règles sans les comprendre, ainsi un mot "illégal" a été supprimé du titre). Malheureusement, cela crée de la confusion.

Je ne suis pas surpris par le fait que l'erreur est générée. Je peux remonter à la source asyncio et voir que les auteurs de cette bibliothèque voulaient le faire de cette façon, il n’ya pas de mystère à ce sujet. Le problème est que les auteurs de la bibliothèque ont décidé qu'il était illégal de demander à une boucle d'événement d'exécuter certaines fonctions lorsque la boucle est déjà en cours d'exécution.

Nous pouvons réduire le problème à deux appels de ce type, et nous verrons, à travers une analyse de cas, qu'il existe trois possibilités:

  1. Aucune des deux fonctions ne se termine jamais.
  2. L'une des fonctions se termine éventuellement.
  3. Les deux fonctions se terminent finalement.

Maintenant, y a-t-il un comportement sain d'esprit qui répondrait aux trois cas? Pour moi, il est évident qu'il existe, ou peut-être plusieurs comportements sains sont possibles ici. Par exemple:

  1. Rien de spécial, l'exécution des deux fonctions est entrelacée et elles continuent à fonctionner pour toujours, comme prévu.
  2. La boucle ne rend pas le contrôle au code suivant la première instance de run_until_complete() jusqu'à la fin de la deuxième fonction (ainsi, aucun code après run_until_complete() ne sera exécuté.
  3. Une fois la dernière fonction terminée, la boucle renvoie le contrôle au premier objet de code qui a appelé run_until_complete en ignorant tous les autres sites d’invocation.

Maintenant, je peux comprendre que ce comportement ne soit pas quelque chose que tout le monde voudrait. Mais puisque cette bibliothèque a décidé de laisser aux programmeurs le contrôle du démarrage/de l’arrêt de la boucle d’événements, elle devrait également faire face aux conséquences de telles décisions. Commencer plusieurs fois par la même boucle par la même boucle empêche le code de la bibliothèque de le faire, ce qui réduit la qualité et l'utilité des bibliothèques utilisant asyncio (ce qui est bien le cas avec, par exemple, aiohttp).

22
wvxvw

Boucle d'événement en cours d'exécution - est un point d'entrée de votre programme async. Il gère l'exécution de toutes les coroutines, tâches, rappels. Exécuter une boucle en cours d'exécution n'a aucun sens: c'est en quelque sorte essayer d'exécuter l'exécuteur de travaux à partir du même exécuteur de travaux en cours d'exécution.

Depuis que vous avez cette question, je suppose que vous pouvez mal comprendre une façon dont asyncio fonctionne. S'il vous plaît, lisez cet article - ce n'est pas grand et donne une bonne introduction.

Upd:

Il n'y a absolument aucun problème à ajouter plusieurs choses à exécuter par boucle d'événement alors que cette boucle est déjà en cours d'exécution. Vous pouvez le faire simplement en l'attendant:

await coro()  # add coro() to be run by event loop blocking flow here until coro() is finished

ou créer une tâche:

asyncio.ensure_future(coro())  # add coro() to be run by event loop without blocking flow here

Comme vous pouvez le constater, vous n'avez pas besoin des méthodes de call event loop pour l'exécuter.

Les méthodes de boucle d'événement telles que run_forever Ou run_until_complete - ne sont qu'un moyen de démarrer une boucle d'événement en général.

run_until_complete(foo()) signifie: "ajoute foo() à exécuter par boucle d'événement et exécute la boucle d'événement elle-même jusqu'à ce que foo() ne soit pas terminé".

15
Mikhail Gerasimov

J'ai résolu le problème en utilisant nest_async

pip install nest_asyncio

et en ajoutant des lignes ci-dessous dans mon fichier.

import nest_asyncio
nest_asyncio.apply()
5
Ovidiu Ionut

J'écris ceci non pas pour faire de la clientèle, mais pour expliquer comment nous pouvons gérer la situation où la mise en file d'attente des fonctions asynchrones et l'attente de leurs résultats de manière synchrone pendant l'exécution de la boucle d'événements ne fonctionnent pas.

run_until_complete ne permet pas l'exécution simultanée d'un nombre quelconque de fonctions asynchrones arbitraires, mais du point d'entrée principal de l'ensemble de votre programme asynchrone. Cette contrainte n'apparaît pas immédiatement dans la documentation.

Comme des bibliothèques comme aiohttp mettent en file d'attente son propre point d'entrée pour s'exécuter en tant que serveur et bloquent les opérations synchrones de la boucle à l'aide de run_until_complete ou run_forever, la boucle d'événement sera déjà en cours d'exécution et vous ne pourrez pas exécuter d'opérations synchrones indépendantes sur cette boucle d'événement et attendre le résultat dans ce thread.

Cela dit, si vous devez mettre en file d'attente une opération asynchrone dans une boucle d'événements en cours à partir d'un contexte de synchronisation et obtenir le résultat obtenu comme une fonction normale, cela risque de ne pas être possible. Votre meilleur choix est de passer un rappel synchrone pour être appelé une fois l'opération asynchrone terminée. Cela ralentira bien sûr votre boucle d'événements.

Une autre façon de gérer la situation consiste à exécuter votre code dans les rappels de démarrage et de nettoyage de la bibliothèque http asynchrone que vous utilisez. Voici un exemple comment vous pouvez y parvenir.

2
nurettin