python asyncio常用方法介绍
-
asyncio.run
# 在当前线程中创建新的事件循环并执行传入的协程
# 源码解读:
def run(main, *, debug=False):
if events._get_running_loop() is not None:
# 从当前线程(threadding.local)中尝试获取正在运行的事件循环,如有事件循环在运行,则报错。
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()
"""
1.每个线程只能挂载一个事件循环。
2.asyncio.run每次都会创建一个新的事件循环去执行任务。在执行完毕后,会将事件循环移出线程,并关闭该事件循环。
""" -
asyncio.new_event_loop & asyncio.set_event_loop
loop = asyncio.new_event_loop() # 在当前线程创建一个新的事件循环。
asyncio.set_event_loop(loop) # 将事件循环设置为当前的默认事件循环,需要注意的是,这里的事件循环还没开始运行。
# set_event_loop源码
def set_event_loop(self, loop):
"""Set the event loop."""
self._local._set_called = True # 记录当前线程的事件循环被设置过
assert loop is None or isinstance(loop, AbstractEventLoop)
self._local._loop = loop # 设置事件循环
"""
1.上面的两个方法常用于手动管理线程的事件循环,手动管理事件循环时,注意调用loop.close()回收资源。
2.需要注意的是,被设置事件循环并没有立刻执行,events._get_running_loop()此时只能获得None。
即,如果要手动管理事件循环,就不要用asyncio.run了,否则asyncio.run会自行创建一个事件循环覆盖掉手动创建的事件循环。
""" -
asyncio.get_event_loop
# 这个方法是获得当前线程中的事件循环
# 源码解读:
## get_event_loop() ##
def get_event_loop():
# NOTE: this function is implemented in C (see _asynciomodule.c)
# 尝试获得正在运行的事件循环,如果有,则返回
current_loop = _get_running_loop()
if current_loop is not None:
return current_loop
# 调用下面展示的方法
return get_event_loop_policy().get_event_loop()
## get_event_loop_policy().get_event_loop() ##
def get_event_loop(self):
# 如果当前线程的事件循环为None且没有设置过事件循环且当前线程是主线程,那么自动创建一个新的事件循环并设置。
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
"""
1.不管当前线程的事件循环是否在运行,都能被get_event_loop获得。
2.如果是在主线程,且从未没有设置过事件循环(结合asyncio.set_event_loop的源码,如果设置过,self._local._set_called会是True),那么这个方法会自动创建新的事件循环并设置。
3.结合前面asyncio.run的源码,可知,先调用asyncio.run后,立即调用asyncio.get_event_loop是会报"找不到事件循环"错误的。
""" -
asyncio.wait_for
# 源码解读:
async def wait_for(fut, timeout, *, loop=None):
# 依赖已创建的事件循环,不会自行创建
if loop is None:
loop = events.get_running_loop()
else:
warnings.warn("The loop argument is deprecated since Python 3.8, "
"and scheduled for removal in Python 3.10.",
DeprecationWarning, stacklevel=2)
# 超时时间设置为None则不限时间
if timeout is None:
return await fut
# 超时时间小于0
if timeout <= 0:
fut = ensure_future(fut, loop=loop)
# 封装成future即完成,则返回结果
if fut.done():
return fut.result()
# 否则取消这个future,通常走这里
fut.cancel()
# 上报超时错误
raise exceptions.TimeoutError()
# 等待对象
waiter = loop.create_future()
# 定时器句柄,在timeout时间后释放等待对象
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
# 偏函数,供任务回调使用,在任务完成时主动释放等待对象
cb = functools.partial(_release_waiter, waiter)
# 这里将任务放到事件循环等待执行,很快就会被自动执行。
fut = ensure_future(fut, loop=loop)
# 注册回调
fut.add_done_callback(cb)
try:
# 等待,直到任务完成或超时
try:
await waiter
except exceptions.CancelledError:
fut.remove_done_callback(cb)
fut.cancel()
raise
if fut.done():
return fut.result()
else:
fut.remove_done_callback(cb)
# We must ensure that the task is not running
# after wait_for() returns.
# See https://bugs.python.org/issue32751
# 这里会向fut发送取消信号,并等待fut结束。
await _cancel_and_wait(fut, loop=loop)
raise exceptions.TimeoutError()
finally:
timeout_handle.cancel()
"""
1.依赖已启动的事件循环,不会自动创建事件循环。
2.如果协程运行超时,wait_for方法尝试取消该协程的future,并等待其结束。
3.wait_for只关注传入协程的future,协程内部通过create_task创建的异步任务需手动管理。如果只创建而不管理,将造成协程泄露。
4.存在密集计算的情况下,适当加入 "await asyncio.sleep(0)"。可以防止某个协程长时间持有"执行权",而其他协程"饿死",也可以让wait_for在超时发生后及时取消任务。
""" -
asyncio.wait
# 这个方法接收一个任务列表,可依据不同的策略去等待这些任务。
# 返回结果是"已完成task集合"和"未完成task集合"。
# 源码解读
async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):
if futures.isfuture(fs) or coroutines.iscoroutine(fs):
raise TypeError(f"expect a list of futures, not {type(fs).__name__}")
if not fs:
raise ValueError('Set of coroutines/Futures is empty.')
# 可选三种终止策略,分别是"任意一个任务完成"、"任意一个任务发生错误"、"所有任务结束"
if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
raise ValueError(f'Invalid return_when value: {return_when}')
if loop is None:
loop = events.get_running_loop()
else:
warnings.warn("The loop argument is deprecated since Python 3.8, "
"and scheduled for removal in Python 3.10.",
DeprecationWarning, stacklevel=2)
# 将任务放到事件循环
fs = {ensure_future(f, loop=loop) for f in set(fs)}
return await _wait(fs, timeout, return_when, loop) -
asyncio.create_task
# 这个方法将协程打包成一个task,并自动将其放到事件循环中等待执行,并且返回这个task供用户await等待。
# 源码
def create_task(coro, *, name=None):
loop = events.get_running_loop()
task = loop.create_task(coro)
_set_task_name(task, name)
return task
"""
create_task创建的Task在放入事件循环后,并没有立刻执行,而是在事件循环下次扫描时执行,通常来说,等待时间很短。
""" -
asyncio.gather
# 这个方法传入多个协程或Future,并发运行并等待结果。
def gather(*coros_or_futures, loop=None, return_exceptions=False):
...
"""
"return_exceptions"参数表示是否将异常作为结果返回,
为False时,如果有任务发生异常,则会传播给gather调用者,提前结束gather命令。
为True时,任务异常会将异常信息做成结果返回出来,不会影响其他任务。
"""
浙公网安备 33010602011771号