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时,任务异常会将异常信息做成结果返回出来,不会影响其他任务。
    """

     

posted @ 2025-04-03 09:40  CJTARRR  阅读(173)  评论(0)    收藏  举报