asyncio多线程RuntimeError

asyncio多线程RuntimeError

背景

在flask中使用loop = asyncio.get_event_loop()

    loop = asyncio.get_event_loop()
  File "/usr/lib64/python3.6/asyncio/events.py", line 694, in get_event_loop
    return get_event_loop_policy().get_event_loop()
  File "/usr/lib64/python3.6/asyncio/events.py", line 602, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-8'.

原因

If there is no current event loop set in the current OS thread, the OS thread is main, and set_event_loop() has not yet been called, asyncio will create a new event loop and set it as the current one.

如果当前没有事件循环和当前线程是主线程,也没有设置事件循环(set_event_loop()),那么会创建一个事件循环并作为当前线程的事件循环,否则RuntimeError: There is no current event loop in thread 'Thread-8'.

loop作用

在主线程中,调用get_event_loop总能返回属于主线程的event loop对象,如果是处于非主线程中,还需要调用set_event_loop方法指定一个event loop对象,这样get_event_loop才会获取到被标记的event loop对象:事件循环是每个异步应用程序的核心。事件循环运行异步任务和回调,执行网络IO操作并运行子流程。

python3.7 获取运行中的事件循环

asyncio.get_running_loop() 返回当前OS线程中的运行事件循环。如果没有正在运行的事件循环,引发则会RuntimeError RuntimeError: no running event loop。只能从协程或回调中调用此函数

if sys.version_info >= (3, 7):
    def get_running_loop() -> AbstractEventLoop: ...
    if sys.version_info < (3, 8):
        class SendfileNotAvailableError(RuntimeError): ...

获取或创建事件循环

asyncio.get_event_loop()获取当前事件循环

Get the current event loop.

If there is no current event loop set in the current OS thread, the OS thread is main, and set_event_loop() has not yet been called, asyncio will create a new event loop and set it as the current one.

Because this function has rather complex behavior (especially when custom event loop policies are in use), using the get_running_loop() function is preferred to get_event_loop() in coroutines and callbacks.

Consider also using the asyncio.run() function instead of using lower level functions to manually create and close an event loop

解决

        # 防止已有loop
        try:
            loop = asyncio.get_event_loop()
        except RuntimeError:
            new_loop = asyncio.new_event_loop()
            asyncio.set_event_loop(new_loop)
            loop = asyncio.get_event_loop()

python3.7

# 考虑使用该asyncio.run()函数,而不要使用较低级别的函数来手动创建和关闭事件循环。
asyncio.run()
>>> import asyncio

>>> async def main():
...     print('hello')
...     await asyncio.sleep(1)
...     print('world')

>>> asyncio.run(main())

多个事件循环

支持使用多个事件循环,比如web服务中日志使用了事件循环,以及接口也有功能需要用到,这样做是可以的,但是不支持事件中嵌套事件,比如接口有个函数A创建了事件循环,这个函数里面又打日志(日志中使用事件循环),那么这种情况会触发以下异常

RuntimeError: Cannot run the event loop while another loop is running

https://bugs.python.org/issue22239

取巧解决方案

使用nest_asyncio

给出需要修补的特定循环作为apply的参数,否则使用当前事件循环。 无论事件循环是否已在运行,都可以对其进行修补。只能修补来自asyncio的事件循环; 来自其他项目(如uvloop或quamash)的循环, 一般不能修补

import nest_asyncio
try:
    loop = asyncio.get_event_loop()
    nest_asyncio.apply(loop)
except RuntimeError:
    new_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(new_loop)
    loop = asyncio.get_event_loop()
    nest_asyncio.apply(loop)

本文作者:朝圣

本文链接:www.zh-noone.cn/2021/1/asyncio多线程RuntimeError

版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0许可协议。转载请注明出处!

python3.6升级3.7问题
0 条评论