Asyncio的事件循环
事件循环
在事件循环中,我们可以:
- 登记,执行和取消调用
- 启动子进程,和外部进程进行通信
- 把耗时的函数调用委托给线程池处理
本质上。所有的事件循环都是等待事件发生,然后执行相应的调用。在此之前,我们需要把即将发生的事件和对应执行的调用相互关联。
事件循环和线程的关系
从asyncio event loop policy文档,我们得知, event loop policy是一个进程全局对象,控制对该进程内所有event loop的管理。
进程的全局policy定义了该policy管控的context的含义,在每个context中管理分开独立的event loop. 默认的policy定义的context就是当前的线程, 也就是说不同的线程是不同的context,因此有不同的event loop。
通过定制event loop policy改变get_event_loop(), set_event_loop(),new_event_loop()的默认行为。
每个context中只有一个running event loop
asyncio中 asyncio.run(),asyncio.get_event_loop(), asyncio.new_event_loop,asyncio.set_event_loop()的场景
asyncio.get_event_loop()
若:
- 当前线程为主线程,
- 当前线程没有启动event loop,
-当前线程没有调用async.set_event_loop(None)
调用asyncio.get_event_loop()方法会生成一个新的默认event loop,并设置为当前线程的事件循环。
此时,get_event_loop()相当于:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
若当前context有默认的event loop,并且没有被set_event_loop(None),则返回默认event loop,
aysncio.run()
调用该方法,会生成新的event loop并在方法调用结束时关闭该event loop。应该作为编写的普通用户异步程序的主入口存在。该方法原则上最好只调用一次。此时asyncio.run()相当于:
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
new_loop.run_until_complete(coro)
asyncio.set_event_loop(None)
new_loop.close()
示例总结:
>>> import asyncio
>>> async def main(loop,desc: str):
... cur_loop = asyncio.get_running_loop()
... if cur_loop is loop:
... print(desc, ': match')
... else:
... print(desc, ': not match')
... print(f'Current running loop is :{id(cur_loop)}' )
>>> loop = asyncio.get_event_loop()
>>> loop2 = asyncio.get_event_loop()
>>>loop is loop2
True
# get_event_loop()取得当前context默认的event loop,若没有则新生成一个。
>>>
>>> loop.run_until_complete(main(loop, 'loop.run_until_complete'))
loop.run_until_complete : match
Current running loop is :140127062748856
>>> loop.close()
#close 当前的默认循环
>>>
>>> loop.run_until_complete(main(loop, 'loop.run_until_complete'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/shone/miniconda2/envs/python37/lib/python3.7/asyncio/base_events.py", line 560, in run_until_complete
self._check_closed()
File "/home/shone/miniconda2/envs/python37/lib/python3.7/asyncio/base_events.py", line 480, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
# close()之后,这个event loop就彻底费了,但是,只要不调用set_event_loop(otherloop), 则loop依然是当前context的默认event loop。
>>>
>>> loop3 = asyncio.new_event_loop()
>>> loop3.run_until_complete(main(loop, 'loop.run_until_complete'))
loop.run_until_complete : not match
Current running loop is :140127033794456
# loop3是新生成的event loop ,但是并非当前context的默认event loop
>>>
>>> loop3.stop()
>>> loop3.is_closed()
False
>>> loop3.run_until_complete(main(loop, 'loop.run_until_complete'))
loop.run_until_complete : not match
Current running loop is :140127033794456
# stop 一个event loop 只是暂停event loop , 还能再用。
>>>
>>> asyncio.set_event_loop(None)
>>> loop5 = asyncio.get_event_loop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/shone/miniconda2/envs/python37/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'MainThread'
# 调用set_event_loop(None) 后,执行get_event_loop会产生例外,
>>>
>>> loop6 = asyncio.new_event_loop()
>>> asyncio.set_event_loop(loop6)
>>> loop7 = asyncio.get_event_loop()
>>> loop6 is loop7
True
# 此时需要给当前context再set一个event loop才行
>>>
>>> loop4.run_until_complete(main(loop6, 'loop.run_until_complete'))
loop.run_until_complete : not match
Current running loop is :140127033408816
# 一个context中可以生成多个event loop,但是默认的event loop 只有一个。并且,只要event loop不被close(), 任何一个event loop都能run,但是正running的event loop只有一个
>>>
>>> asyncio.run(main(loop6, 'asyncio.run'))
asyncio.run : not match
Current running loop is :140127033529008
>>> loop8 = asyncio.get_event_loop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/shone/miniconda2/envs/python37/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'MainThread'.
# 调用asyncio.run()相当于从新设定当前contex的默认loop , 使用完后,close当前的默认loop
>>>
以上代码实例,来源自
https://gist.github.com/kaelz...
https://www.programcreek.com/...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。