Python 3.8异步并发编程指南

有效的提高程序执行效率的两种方法是异步和并发,Golang,node.js之所以可以有很高执行效率主要是他们的协程和异步并发机制 。实际上异步和并发是每一种现代语言都在追求的特性,当然Python也不例外,今天我们就讲讲Python 3中的异步并发编程 。

Python 3.8异步并发编程指南

文章插图
 
概述Python 标准库提供了许多模块来处理异步并发和多进程任务,包括:
本文中,我们主要以于asyncio和 concurrent.futures为主讲讲Python中异步和并发机制及实例 。当然你也可能正在使用_thread和threading,但是无疑最应用用multiprocessing和concurrent.futures 。
基本理论异步编程编写并发代码(使用_thread或threading模块)的是为了解决大家对CPU调用和中断的问题(因为CPU中一次只能运行一个线程),这涉及了"CPU 上下文切换"的成本,虽然速度很快,但是也需要消耗资源 。而且还要处理"条件竞争","死锁/实时"和"资源不足"(某些线程被过度使用,而其他线程未被充分利用)等等的问题 。而这些正是异步任务可以避免的 。
asyncioasyncio是一个标准库,用于使用asyn/cawait语法编写并发代码 。asyncio模块分别提供了高级和低级 API 。库和框架开发人员将使用低级 API,同时鼓励使用不同于更传统threading或multiprocess异步代码执行方法 。它利用事件循环的来处理异步"任务"的调度,而不是传统的线程或子进程 。
需要提及的是asyncio是为了解决 I/O 性能,而不是CPU 绑定操作 。因此,asyncio不是所有类型的异步执行的替代品 。
asyncio基于"合作多任务"的概念设计的,因此可以完全控制 CPU上下文切换发生的时间(即上下文切换发生在应用程序级别,而不是硬件级别) 。
使用asyncio,Python调度器负责管理,因此应用程序可能随时进行上下文切换) 。
所以使用asyncio时,还需要使用某种形式的"锁定"机制来防止多个线程访问/更改共享内存(否则可能会破坏程序的线程安全) 。
concurrent.futuresconcurrent.futures 模块是为异步执行可调用项提供高级接口,为thread 和multiprocessing模块提供了高级抽象,这就是为什么本文没有详细讨论这些模块的原因 。事实上,_thread模块是一个非常低级别的 API,_threading模块本身是建立在它之上的 。
前面我们已经提到,异步可以帮助我们避免使用线程,那么,如果它只是线程(和多处理)上的抽象,那么为什么要使用concurrent.futures呢?嗯,因为并非所有库/模块/API都支持异步模型 。
例如,如果使用boto3和 AWS S3 并与之交互,就会发现这些是同步操作 。可以在多线程代码中包装这些调用,但最好使用concurrent.futures,因为这样不仅受益于传统线程,而且受益于异步友好特性 。该模块还设计为和异步事件循环互操作,从而更轻松地在异步驱动应用程序中处理线程/子进程池 。
此外,当需要线程池或子进程池时,还需要利用concurrent.futures,同时可以使用干净而现代的 Python API 。
绿色线程实现异步编程的方法有很多种 。有事件循环方法(异步实现),这种"回调"风格是 JAVAScript 等单线程语言一直采用的方法 。传统上还有一个称为"绿色线程"的概念 。
从本质上讲,绿色线程的外观和感觉与普通线程完全一样,只不过线程是由应用程序代码而不是硬件安排的 。因此,可以有效地处理(与事件循环一样)的确定性上下文切换问题 。但是处理共享内存同样存在问题 。
因此,让我们现在快速看看什么是"事件循环",因为它是使异步工作的基础,为什么我们可以避免"回调地狱"和与"绿色线程"固有的问题...
事件循环所有异步应用程序的核心元素是"事件循环" 。事件循环是计划并运行异步任务的内容,它还包括处理网络 IO 操作和子进程运行 。
Python 3.8异步并发编程指南

文章插图
 
异步事件循环非常有效,是因为它由Python内部在生成器实现的 。生成器使函数能够部分执行,然后在特定点停止,维护一堆对象和异常,然后再恢复 。
可等待性异步背后的驱动力是计划异步"任务"的能力 。Python 中有几个不同类型的对象有助于支持此功能,它们通常通过Awaitables(可等待)进行分组 。
如果某物可以在表达式中使用,它是可以等待的 。await有三种主要的等待类型:
协程、任务和Future 。


推荐阅读