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

 

 

 

posted on 2020-08-20 15:45  麦克煎蛋  阅读(945)  评论(0编辑  收藏  举报