web-dev-qa-db-fra.com

RuntimeError: il n'y a pas de boucle d'événement en cours dans le thread async + apscheduler

J'ai une fonction asynchrone et j'ai besoin de courir avec apscheduller toutes les N minutes. Il y a un code python ci-dessous)

URL_LIST = ['<url1>',
            '<url2>',
            '<url2>',
            ]

def demo_async(urls):
    """Fetch list of web pages asynchronously."""
    loop = asyncio.get_event_loop() # event loop
    future = asyncio.ensure_future(fetch_all(urls)) # tasks to do
    loop.run_until_complete(future) # loop until done

async def fetch_all(urls):
    tasks = [] # dictionary of start times for each url
    async with ClientSession() as session:
        for url in urls:
            task = asyncio.ensure_future(fetch(url, session))
            tasks.append(task) # create list of tasks
        _ = await asyncio.gather(*tasks) # gather task responses

async def fetch(url, session):
    """Fetch a url, using specified ClientSession."""
    async with session.get(url) as response:
        resp = await response.read()
        print(resp)

if __name__ == '__main__':
    scheduler = AsyncIOScheduler()
    scheduler.add_job(demo_async, args=[URL_LIST], trigger='interval', seconds=15)
    scheduler.start()
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))

    # Execution will block here until Ctrl+C (Ctrl+Break on Windows) is pressed.
    try:
        asyncio.get_event_loop().run_forever()
    except (KeyboardInterrupt, SystemExit):
        pass

Mais quand j'ai essayé de le lancer, j'ai les informations d'erreur suivantes

Job "demo_async (trigger: interval[0:00:15], next run at: 2017-10-12 18:21:12 +04)" raised an exception.....
..........\lib\asyncio\events.py", line 584, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread '<concurrent.futures.thread.ThreadPoolExecutor object at 0x0356B150>_0'.

Pourriez-vous s'il vous plaît m'aider avec cela? Python 3.6, APScheduler 3.3.1,

22
Valera Shutylev

Il suffit de passer fetch_all À scheduler.add_job() directement. Le planificateur asyncio prend en charge les fonctions de coroutine en tant que cibles de travail.

Si l'appelable cible est pas une fonction de coroutine, il sera exécuté dans un thread de travail (pour des raisons historiques), d'où l'exception.

5
Alex Grönholm

Dans votre def demo_async(urls), essayez de remplacer:

loop = asyncio.get_event_loop()

avec:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
48
prezha

La chose importante qui n'a pas été mentionnée est la raison pour laquelle l'erreur se produit. Pour moi personnellement, il est aussi important de savoir pourquoi l'erreur se produit que de résoudre le problème.

Jetons un coup d'œil à l'implémentation du get_event_loop De BaseDefaultEventLoopPolicy:

class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
    ...

    def get_event_loop(self):
        """Get the event loop.

        This may be None or an instance of EventLoop.
        """
        if (self._local._loop is None and
            not self._local._set_called and
            isinstance(threading.current_thread(), threading._MainThread)):
            self.set_event_loop(self.new_event_loop())
        if self._local._loop is None:
            raise RuntimeError('There is no current event loop in thread %r.'
                               % threading.current_thread().name)
        return self._local._loop

Vous pouvez voir que la self.set_event_loop(self.new_event_loop()) n'est exécutée que si toutes les conditions ci-dessous sont remplies:

  • self._local._loop is None - _local._loop N'est pas défini
  • not self._local._set_called - set_event_loop N'a pas encore été appelé
  • isinstance(threading.current_thread(), threading._MainThread) - le thread actuel est le principal (ce n'est pas vrai dans votre cas)

Par conséquent, l'exception est déclenchée, car aucune boucle n'est définie dans le thread actuel:

if self._local._loop is None:
    raise RuntimeError('There is no current event loop in thread %r.'
                       % threading.current_thread().name)
5
Jatimir

Comme cette question continue d'apparaître sur la première page, j'écrirai mon problème et ma réponse ici.

J'avais un RuntimeError: There is no current event loop in thread 'Thread-X'. En utilisant flask-socketio et Bleak .


Edit: eh bien, j'ai refactoré mon fichier et créé un cours.

J'ai initialisé la boucle dans le constructeur, et maintenant tout fonctionne bien:

class BLE:
    def __init__(self):
        self.loop = asyncio.get_event_loop()

    # function example, improvement of
    # https://github.com/hbldh/bleak/blob/master/examples/discover.py :
    def list_bluetooth_low_energy(self) -> list:
        async def run() -> list:
            BLElist = []
            devices = await bleak.discover()
            for d in devices:
                BLElist.append(d.name)
            return 'success', BLElist
        return self.loop.run_until_complete(run())

Usage:

ble = path.to.lib.BLE()
list = ble.list_bluetooth_low_energy()

Réponse originale:

La solution était stupide. Je n'ai pas fait attention à ce que j'ai fait, mais j'ai déplacé certains import d'une fonction, comme ceci:

import asyncio, platform
from bleak import discover

def listBLE() -> dict:
    async def run() -> dict:
        # my code that keep throwing exceptions.

    loop = asyncio.get_event_loop()
    ble_list = loop.run_until_complete(run())
    return ble_list

J'ai donc pensé que je devais changer quelque chose dans mon code et j'ai créé une nouvelle boucle d'événement en utilisant ce morceau de code juste avant la ligne avec get_event_loop():

loop = asyncio.new_event_loop()
loop = asyncio.set_event_loop()

À ce moment-là, j'étais plutôt heureux, car j'avais une boucle en marche.

Mais ne répond pas. Et mon code s’appuyait sur un délai d’attente pour renvoyer des valeurs, c’était donc très mauvais pour mon application.

Il m'a fallu près de deux heures pour comprendre que le problème était le import, et voici mon code (de travail):

def list() -> dict:
    import asyncio, platform
    from bleak import discover

    async def run() -> dict:
        # my code running perfectly

    loop = asyncio.get_event_loop()
    ble_list  = loop.run_until_complete(run())
    return ble_list
0
sodimel

Utilisez asyncio.run() au lieu d'utiliser directement la boucle d'événement. Il crée une nouvelle boucle et la ferme lorsque vous avez terminé.

Voici à quoi ressemble le 'run':

if events._get_running_loop() is not None:
    raise RuntimeError(
        "asyncio.run() cannot be called from a running event loop")

if not coroutines.iscoroutine(main):
    raise ValueError("a coroutine was expected, got {!r}".format(main))

loop = events.new_event_loop()
try:
    events.set_event_loop(loop)
    loop.set_debug(debug)
    return loop.run_until_complete(main)
finally:
    try:
        _cancel_all_tasks(loop)
        loop.run_until_complete(loop.shutdown_asyncgens())
    finally:
        events.set_event_loop(None)
        loop.close()
0
Asaf Pinhassi