RuntimeError:async + apscheduler中的线程中没有当前事件循环

时间:2017-10-13 10:27:22

标签: python python-3.x python-asyncio aiohttp apscheduler

我有一个异步功能,需要每隔N分钟使用apscheduller运行一次。

下面有一个python代码
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

但是当我试图运行它时,我有下一个错误信息

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'.
你可以帮我解决这个问题吗? Python 3.6,APScheduler 3.3.1,

5 个答案:

答案 0 :(得分:45)

def demo_async(urls)中,尝试替换:

loop = asyncio.get_event_loop()

使用:

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

答案 1 :(得分:4)

直接将fetch_all传递给scheduler.add_job()。 asyncio调度程序支持协程功能作为作业目标。

如果目标可调用一个协程函数,它将在一个工作线程中运行(由于历史原因),因此是异常。

答案 2 :(得分:2)

尚未提及的重要事情是为什么发生错误。对我个人而言,知道为什么会发生错误与解决实际问题同等重要。

我们来看看System.exit(0); get_event_loop的实现:

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 仅在满足以下所有条件时才执行:

  • self.set_event_loop(self.new_event_loop())-未设置self._local._loop is None
  • _local._loop-尚未not self._local._set_called被呼叫
  • set_event_loop-当前线程是主要线程(在您的情况下,这不是True)

因此引发异常,因为在当前线程中未设置任何循环:

isinstance(threading.current_thread(), threading._MainThread)

答案 3 :(得分:0)

使用asyncio.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()

答案 4 :(得分:0)

由于此问题继续出现在首页上,因此我将在此处写下我的问题和答案。

使用flask-socketioBleak时,我有一个RuntimeError: There is no current event loop in thread 'Thread-X'.


编辑:好,我重构了文件并制作了一个类。

我在构造函数中初始化了循环,现在一切正常:

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())

用法:

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

原始答案:

解决方案很愚蠢。我没有注意自己所做的事情,但我将一些import从函数中移出了,像这样:

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

因此,我认为我需要更改代码中的某些内容,并在get_event_loop()行的前面使用这段代码创建了一个新的事件循环:

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

此刻我很高兴,因为我运行了一个循环。

但没有回应。而且我的代码依靠超时来返回一些值,因此这对我的应用程序来说是非常糟糕的。

我花了将近两个小时才弄清楚问题出在import上,这是我的(有效的)代码:

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