🗒️Python asyncio高性能异步编程2
00 分钟
2024-8-27
2024-8-27
type
status
date
slug
summary
tags
category
icon
password

事件循环

理解成一个死循环,去检测去执行某些代码
伪代码
notion image

快速上手

协程函数:async def函数名
协程对象:协程函数+(),执行协程函数,得到的就是协程对象
 
注意:执行协程函数创建协程对象,函数内部代码并不会被执行,要执行函数必须和事件循环搭配起来使用
 
 

await

await+可等待的对象(协程对象,future对象,Task对象 都可以理解成====⇒ io等待)
 
案例1
 
案例2
 
案例3
await 关键字在等待对应的值返回结果后再继续执行后续代码。不过,你可能会觉得,这听起来像是同步执行——即一个任务执行完后再执行下一个任务。实际上,情况并非如此。当代码遇到 await 时,它会暂停当前的 async 函数的执行,等待结果返回,但在此期间,Python 解释器可以继续执行其他的 async 函数,从而实现异步操作。

 

执行协程函数的顺序由什么决定?

在 Python 中,await 的工作原理涉及到事件循环(Event Loop)的调度机制。事件循环负责调度和执行多个协程(coroutines),使得这些协程可以并发地运行。它的核心思想是,当一个协程遇到 await 时,它会将控制权返回给事件循环,事件循环可以选择运行其他处于就绪状态的协程。
我来举个简单的例子,帮助你理解这个过程。

解释:

  1. 任务启动:在 main() 函数中,我们通过 asyncio.gather() 同时启动了 task1()task2() 这两个异步任务。
  1. 任务执行
      • task1() 开始执行并打印 "Task 1: Started"。
      • task2() 开始执行并打印 "Task 2: Started"。
  1. await 暂停
      • task1() 遇到 await asyncio.sleep(2) 时,会暂停执行,等待 2 秒。
      • 事件循环立即切换去执行 task2(),因为 task2() 只需要等待 1 秒钟,因此 task2() 会先完成。
  1. 任务完成
      • task2() 先打印 "Task 2: Finished after 1 second",并结束。
      • task2() 结束后,事件循环返回执行 task1(),2 秒等待时间结束后,task1() 打印 "Task 1: Finished after 2 seconds"。

总结:

  • 事件循环根据哪个任务的 await 状态来决定执行哪个 async 函数。在 await 过程中,事件循环会去执行其他就绪的协程。
  • 这个例子中,task2() 先完成,因为它的 await 时间较短。事件循环让 task2() 完成后,再回到 task1() 完成它的等待操作。
希望这个例子帮助你更好地理解 asyncawait 的执行机制!
 

Task对象

在事件循环中添加多个任务
notion image
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task()函数以外,还可以用低层级的1oop.create_task()或 ensure_future()函数。不建议手动实例化 Task 对象。
 
示例1
 
示例2
 
在 Python 的 asyncio 模块中,asyncio.create_task()asyncio.wait()asyncio.run() 是常用的函数,它们各自有特定的用途和使用场景。下面是对它们的解释以及它们接收的参数类型:

1. asyncio.create_task()

  • 作用: 这个函数用于创建一个新的任务(即协程对象的调度执行)。它将一个协程包裹成一个任务,并提交给事件循环处理。任务会立即启动并异步执行。
  • 参数:
    • 接收一个协程对象(coroutine)作为参数,例如 func()
  • 返回值: 返回一个 Task 对象,表示正在进行的异步操作。
  • 示例:

    2. asyncio.wait()

    • 作用: 这个函数用于等待一个或多个协程(或任务)完成。可以选择等待所有任务完成,或者当其中任意一个任务完成时就返回。
    • 参数:
      • 接收一个可迭代的协程或任务(例如 task_list)作为参数。
      • 可选参数 timeout 可以指定等待的超时时间。
      • 可选参数 return_when 可以指定何时返回(如 asyncio.ALL_COMPLETEDasyncio.FIRST_COMPLETED)。
    • 返回值: 返回一个包含两个集合的元组 (done, pending)done 包含已经完成的任务,pending 包含尚未完成的任务。
    • 示例:

      3. asyncio.run()

      • 作用: 这个函数用于运行整个异步程序。它接收一个协程对象,将其运行直至完成,适用于在顶层启动异步代码。
      • 参数:
        • 接收一个协程对象(coroutine),通常是 async 函数的调用(例如 main())。
      • 返回值: 返回协程运行完成后的结果。
      • 示例:

        总结

        • asyncio.create_task():用于启动一个协程并让它异步执行,适用于并发执行多个任务的情况。
        • asyncio.wait():用于等待多个任务的完成,可以指定等待所有任务完成或只等待第一个完成。
        • asyncio.run():用于启动并运行整个异步程序,适用于主程序的入口。
        这些函数可以组合使用,实现复杂的异步任务调度和并发控制。

        asynic.future对象

        Future 对象概述

        在 Python 的 asyncio 模块中,Future 对象代表一个异步操作的最终结果。它类似于 JavaScript 中的 Promise。一个 Future 对象并不执行任何实际的计算,它只是一个容器,用于存储异步操作的最终结果或异常。
        • 未完成状态: 当一个 Future 对象被创建时,它通常处于“未完成”状态,这意味着结果还不可用。
        • 完成状态: 当异步操作完成后,Future 对象的状态会变为“完成”,此时可以从 Future 对象中获取结果。
        Future 对象通常不需要直接创建,因为高层次的 asyncio API 会自动处理它们。但理解 Future 是如何工作的,对于深入掌握 Python 的异步编程模型非常重要。

        Future 对象的使用场景

        Future 对象在以下场景中很有用:
        • 协程与回调之间的桥梁: 你可以使用 Future 对象将一个异步操作的结果传递给一个回调函数。
        • 手动管理异步任务的结果: 在某些高级用例中,你可能希望手动创建和操作 Future 对象。

        举个简单的例子

        以下是一个使用 Future 对象的简单例子。这个例子模拟了一个异步操作,该操作在完成后手动设置 Future 对象的结果:

        代码解析

        1. 创建 Future 对象:
          1. 在这个例子中,我们通过事件循环创建了一个 Future 对象。此时,future 处于“未完成”状态。
        1. 异步设置 Future 对象的结果:
          1. 我们启动了一个异步任务 set_future_value(future),它将在 2 秒后通过 future.set_result('操作完成!') 来设置 Future 对象的结果。
        1. 等待 Future 对象的结果:
          1. main() 函数中,我们使用 await 来等待 Future 对象的结果。当结果被设置后,await future 语句会继续执行,result 变量将会接收到 future 的结果。
        1. 最终输出:
          1. Future 对象完成后,结果会被打印出来。

        总结

        • Future 对象代表了一个尚未完成的异步操作的结果。
        • 你可以通过 set_result() 方法手动设置 Future 对象的结果。
        • 使用 await future 可以等待 Future 对象完成并获取其结果。
        通过这个简单的例子,你可以看到如何使用 Future 对象来管理和获取异步操作的结果。在实际应用中,很多时候你不需要直接使用 Future 对象,因为 asyncio 提供的高层 API 已经封装了这些逻辑。
         

         

        concurrent的Future对象

        concurrent.futures 是 Python 标准库中的一个模块,它提供了一些工具来执行并发任务。这个模块中也有一个 Future 对象,它的作用和 asyncio.Future 类似,但用于线程池或进程池的并发任务管理。

        concurrent.futures.Future 的作用

        • 状态管理: Future 对象用于管理异步或并发任务的执行状态和结果。
        • 任务完成通知: Future 对象可以让你查询任务是否完成,并在任务完成后获取结果或捕获异常。
        • 回调机制: 你可以向 Future 对象添加回调函数,当任务完成时,回调函数会被调用。

        Future 的状态

        • 未完成状态: 当一个任务被提交给线程池或进程池时,Future 对象处于“未完成”状态。
        • 完成状态: 当任务完成时,无论是成功还是失败,Future 对象的状态都会变为“完成”。

        举个简单的例子

        下面是一个使用 concurrent.futures 模块的 ThreadPoolExecutorFuture 对象的简单例子。我们将在线程池中提交一个任务,并使用 Future 对象来获取任务的结果。

        代码解析

        1. 任务函数 task:
          1. 这是一个模拟的任务函数,它接收一个秒数作为参数,等待指定的秒数,然后返回一个完成消息。
        1. 创建线程池执行器:
          1. 这里我们使用 ThreadPoolExecutor 创建一个线程池执行器。ThreadPoolExecutor 可以管理和调度多个线程并行执行任务。
        1. 提交任务并获取 Future 对象:
          1. 我们将任务 task 提交给线程池,任务会在后台执行。submit 方法返回一个 Future 对象,该对象代表任务的执行结果或状态。
        1. 执行其他操作:
          1. 任务在后台运行时,主线程可以继续执行其他操作。这展示了并发执行的优势。
        1. 等待任务完成并获取结果:
          1. 通过调用 future.result(),我们可以阻塞并等待任务完成。一旦任务完成,这个方法将返回任务的结果。如果任务抛出了异常,这个方法会重新抛出该异常。

        总结

        • concurrent.futures.Future 对象用于管理线程池或进程池中的并发任务。
        • 你可以通过 executor.submit() 提交任务,并获取一个 Future 对象。
        • 通过 future.result(),你可以等待任务完成,并获取结果。
        这个例子展示了如何使用 concurrent.futures.Future 对象来执行和管理并发任务,同时也展示了线程池执行器的基本使用方式。
         

         

        异步和非异步模块混合案例

        案例:使用asynicio去结合使用不支持异步的模块
        效果一摸一样的,但是耗费的资源更多
         
         
         

        打赏

        如果您觉得我的内容对你有所帮助,不要吝啬你的一键三连!如果你有能力的话也可以通过下面请我喝杯咖啡~金额您随意~如果对文章内容有任何疑问,欢迎加入群组联系我~
        notion image
        上一篇
        Python asyncio高性能异步编程1—协程
        下一篇
        Python asyncio高性能异步编程3—异步迭代器