51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Python 异步执行 Asyncio

异步和高并发在一些服务器场景需求很多,从2013年起由 Python 之父 Guido 亲自操刀主持了Tulip(asyncio)项目的开发,使得 Python 具备了优雅的异步编程库。

简介 {#简介}

  • 之前介绍了使用 Python Threading 实现异步编程,使用的是多线程的思路,语法不够优雅而且对于简单的工作来说还是太重了。

  • 比较合适的程序运行单位叫做协程,协程能够在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能。

协程 {#协程}

协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。

  • Asyncio 并不能带来真正的并行(parallelism)。当然,因为 GIL(全局解释器锁)的存在,Python 的多线程也不能带来真正的并行。可交给 asyncio 执行的任务,称为协程(coroutine)。一个协程可以放弃执行,把机会让给其它协程(即 yield from 或 await)。

  • 在Python中有多种方式可以实现协程,例如:

  1. greenlet,是一个第三方模块,用于实现协程代码(Gevent协程就是基于greenlet实现);
  2. yield,生成器,借助生成器的特点也可以实现协程代码;
  3. asyncio,在 Python3.4 中引入的模块用于编写协程代码;
  4. async & awiat,在 Python3.5 中引入的两个关键字,结合 asyncio 模块可以更方便的编写协程代码。

目前python异步相关的主流技术是通过包含关键字async&await的async模块实现,因此我们重点关注 asyncio 模块。

asyncio {#asyncio}

asyncio 是 Python 3.4 版本引入的标准库,直接内置了对异步IO的支持。

代码示例 {#代码示例}
  • 示例 1
  • 示例 2
  • 示例 3

输出:一次发送三个下载请求,同时下载,假如每次下载花费1s,完成任务仅需要1s 左右

async 关键字 {#async-关键字}

async & await关键字在 Python 3.5 版本中正式引入,代替了asyncio.coroutine 装饰器,基于他编写的协程代码其实就是上一示例的加强版,让代码可以更加简便可读。

  • 协程函数:定义函数时候由async关键字装饰的函数 async def 函数名 (定义协程)
  • 协程对象:执行协程函数得到的协程对象。

注意:执行协程函数只会创建协程对象,函数内部代码不会执行。如果想要运行协程函数内部代码,必须要将协程对象交给事件循环来处理。

  • 协程能做的事情
    • 等待一个 future 结束
    • 等待另一个协程(产生一个结果,或引发一个异常)
    • 产生一个结果给正在等它的协程
    • 引发一个异常给正在等它的协程

await 关键字 {#await-关键字}

await + 可等待的对象(协程对象、Future、Task对象 -> IO等待),遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。

task 对象 {#task-对象}

Task 对象的作用是在事件循环中添加多个任务,用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。

future 回调 {#future-回调}

假如协程是一个 IO 的读操作,等它读完数据后,我们希望得到通知,以便下一步数据的处理。这一需求可以通过往 future 添加回调来实现。

多个协程 {#多个协程}

实际项目中,往往有多个协程,同时在一个 loop 里运行。为了把多个协程交给 loop,需要借助 asyncio.gather 函数。

官方文档:

或者先把协程存在列表里:

也可以传 futures 给它:

gather 起聚合的作用,把多个 futures 包装成单个 future,因为 loop.run_until_complete 只接受单个 future。

Close Loop {#Close-Loop}

以上示例都没有调用 loop.close,好像也没有什么问题。所以到底要不要调 loop.close 呢?
简单来说,loop 只要不关闭,就还可以再运行。:

但是如果关闭了,就不能再运行了:

建议调用 loop.close,以彻底清理 loop 对象防止误用。

多进程+asyncio {#多进程-asyncio}

由于python本身只能单线程,所以所谓的线程是通过线程锁实现的。现在必须要通过多进程实现更多的并发。

现在demo实现了使用多进程,每个进程都有一个asyncio

gather 起聚合的作用,把多个 futures 包装成单个 future,因为 loop.run_until_complete 只接受单个 future。

参考资料 {#参考资料}



文章链接:
https://www.zywvvd.com/notes/coding/python/asyncio/asyncio/

赞(1)
未经允许不得转载:工具盒子 » Python 异步执行 Asyncio