• Python协程(二) Asyncio入门


    Asyncio模块提供了使用协程构建并发应用的工具。它使用一种单线程的方式实现并发,一般会在程序阻塞I/O操作的时候发生上下文切换,如读写文件,或者请求网络。

    同时Asyncio也支持调度代码在将来的某个特定事件运行,从而支持一个协程等待另一个协程完成,以处理系统信号和识别其他一些事件。

    基本概念

    Asyncio里面主要以下几个需要关注的基本概念。

    1、Eventloop(事件循环)

    事件循环是每个 Asyncio 应用的核心。 事件循环会运行异步任务和回调,执行网络 IO 操作,以及运行子进程。

    程序开启一个无限循环,并且把一些协程函数注册到这个事件循环上,事件循环会循环执行这些函数 (但同时只能执行一个),当执行到某个函数时,如果它正在等待 I/O 返回,事件循环会暂停它的执行去执行其他的函数;当某个函数完成 I/O 后会恢复,下次循环到它的时候继续执行。因此,这些异步函数可以协同 (Cooperative) 运行:这就是事件循环的目标。

    2、可等待对象

    如果一个对象可以在 await 语句中使用,那么它就是 可等待 对象。许多 Asyncio API 都被设计为接受可等待对象。

    可等待 对象有三种主要类型: 协程Future 和 任务.

    3、协程 (Coroutine)

    协程 (Coroutine) 本质上是一个函数,特点是在代码块中可以将执行权交给其他协程。

    它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环中,由事件循环调用。

    协程通过 async/await 语法进行声明,是编写 Asyncio 应用的推荐方式。 例如,以下代码段(需要 Python 3.7+)会打印 "hello",等待 1 秒,再打印 "world":

    >>> import asyncio
    
    >>> async def main():
    ...     print('hello')
    ...     await asyncio.sleep(1)
    ...     print('world')
    
    >>> asyncio.run(main())
    hello
    world

    注意:简单地调用一个协程并不会将其加入执行日程。

    要真正运行一个协程,asyncio 提供了三种主要机制:

    • asyncio.run() 函数用来运行最高层级的入口点 "main()" 函数 (参见上面的示例。)

    • 等待一个协程。以下代码段会在等待 1 秒后打印 "hello",然后 再次 等待 2 秒后打印 "world":

    • import asyncio
      import time
      
      async def say_after(delay, what):
          await asyncio.sleep(delay)
          print(what)
      
      async def main():
          print(f"started at {time.strftime('%X')}")
      
          await say_after(1, 'hello')
          await say_after(2, 'world')
      
          print(f"finished at {time.strftime('%X')}")
      
      asyncio.run(main())
      
      # 运行结果
      started at 10:38:33
      hello
      world
      finished at 10:38:36
    • asyncio.create_task() 函数用来并发运行作为 asyncio 任务 的多个协程。我们修改以上示例,并发 运行两个 say_after 协程:
    • async def main():
          task1 = asyncio.create_task(say_after(1, 'hello'))
      
          task2 = asyncio.create_task(say_after(2, 'world'))
      
          print(f"started at {time.strftime('%X')}")
      
          # Wait until both tasks are completed (should take
          # around 2 seconds.)
          await task1
          await task2
      
          print(f"finished at {time.strftime('%X')}")
      
      # 运行结果(注意,输出显示代码段的运行时间比之前快了 1 秒:)
      started at 10:40:58
      hello
      world
      finished at 10:41:00

    4、Future

    Future 是一种特殊的 低层级 可等待对象,表示一个异步操作的 最终结果

    当一个 Future 对象 被等待,这意味着协程将保持等待直到该 Future 对象在其他地方操作完毕或取消。

    Future 对象通常用来链接 底层回调式代码 和高层异步/等待式代码,在 Asyncio 中需要 Future 对象以便允许通过 async/await 使用基于回调的代码。

    事件循环可以监视Future对象是否完成。从而允许应用的一部分等待另一部分完成一些工作。

    异步操作结束后会把最终结果设置到这个 Future 对象上。Future 是对协程的封装,通常情况下 没有必要 在应用层级的代码中创建 Future 对象。

    Future 对象有时会由库和某些 asyncio API 暴露给用户,用作可等待对象:

    async def main():
        await function_that_returns_a_future_object()
    
        # this is also valid:
        await asyncio.gather(
            function_that_returns_a_future_object(),
            some_python_coroutine()
        )

    5、Task

    Task是Future的一个子类,它用来包装和管理一个协程的执行。任务所需的资源可用时,事件循环会调度任务允许,并生成一个结果,从而可以由其他协程消费。

    Task 对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future 对象,Task 对象会挂起该协程的执行并等待该 Future 对象完成。当该 Future 对象 完成,被打包的协程将恢复执行。

    事件循环使用协同日程调度: 一个事件循环每次运行一个 Task 对象。而一个 Task 对象会等待一个 Future 对象完成,该事件循环会运行其他 Task、回调或执行 IO 操作。

    使用高层级的 asyncio.create_task() 函数来创建 Task 对象,也可用低层级的 loop.create_task() 或 ensure_future() 函数。不建议手动实例化 Task 对象。

    要取消一个正在运行的 Task 对象可使用 cancel() 方法。调用此方法将使该 Task 对象抛出一个 CancelledError 异常给打包的协程。如果取消期间一个协程正在等待一个 Future 对象,该 Future 对象也将被取消。

     

    参考文章:

    https://zhuanlan.zhihu.com/p/69210021
    https://segmentfault.com/q/1010000007863343
    https://www.jianshu.com/p/2afbe455b526
    https://blog.csdn.net/weixin_45139605/article/details/90798253
    https://blog.csdn.net/weixin_41599977/article/details/93656042
    https://docs.python.org/zh-cn/3/library/asyncio-eventloop.html#creating-futures-and-tasks
    https://python-parallel-programmning-cookbook.readthedocs.io/zh_CN/latest/chapter4/03_Event_loop_management_with_Asyncio.html
    https://learnku.com/docs/pymotw/asyncio-asynchronous-io-event-loop-and-concurrency-tools/3423
    https://docs.python.org/zh-cn/3/library/asyncio-task.html
    http://www.manongjc.com/article/75292.html
    https://segmentfault.com/a/1190000012631063
    https://www.dongwm.com/post/142/
    https://www.jianshu.com/p/71b90a578668
    https://realpython.com/async-io-python/#the-event-loop-and-asynciorun

     

  • 相关阅读:
    SQL 的单引号转义字符
    Oracle中row_number()、rank()、dense_rank() 的区别
    Hibernate之mappedBy与@JoinColumn
    spring定时任务的注解实现方式
    springmvc常用注解之@Controller和@RequestMapping
    out.print()与out.write()的区别
    idea 中 找不到程序包 的坑
    Thymeleaf学习
    unknow command 'iscan',使用redis desktop manager 无法读取redis存储的数据
    Struts2框架简单介绍
  • 原文地址:https://www.cnblogs.com/mazhiyong/p/13517799.html
Copyright © 2020-2023  润新知