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)