python3异步
Python是支持某种方式编写异步程序的语言之一,这些程序可以在多个任务之间自由切换,这些任务一次运行,因此没有一个任务可以阻止其他任务的进行。
不过,您可能主要是编写了同步Python程序,这些程序一次只做一件事,等待每个任务完成,然后再启动另一个。 转向异步可能会很麻烦,因为它不仅需要学习新的语法,还需要学习思考代码的新方法。
在本文中,我们将探讨如何将现有的同步程序转换为异步程序。 这不仅仅是使用异步语法来装饰函数; 它还需要对程序的运行方式进行不同的思考,并确定异步是否甚至可以很好地隐喻其功能。
[ 也在InfoWorld上:从Serdar Yegulalp的Smart Python视频中学习Python技巧和窍门 ]
何时在Python中使用异步
具有以下特征的Python程序最适合异步:
- 它正在尝试执行大部分受I / O约束或等待某些外部过程完成的事情,例如长时间运行的网络读取。
- 它试图同时执行一项或多项此类任务,同时可能还要处理用户交互。
- 所讨论的任务在计算上并不繁重。
使用线程的Python程序通常是使用异步的一个不错的选择。 Python中的线程是协作的; 他们根据需要互相屈服。 Python中的异步任务以相同的方式工作。 另外,异步提供了一些优于线程的优势:
-
async
/await
语法使识别程序的异步部分变得容易。 相比之下,通常很难一眼就看出应用程序的哪些部分在线程中运行。 - 由于异步任务共享同一线程,因此它们访问的任何数据均由GIL(用于同步对对象的访问的Python本机机制)自动管理。 线程通常需要复杂的同步机制。
- 异步任务比线程更容易管理和取消 。
如果您的Python程序具有以下特征, 则不建议使用async:
- 这些任务具有很高的计算成本,例如,它们正在做大量的数字运算。 最好使用
multiprocessing
处理来处理繁重的计算工作,这使您可以将整个硬件线程专用于每个任务。 - 任务无法从交错中受益。 如果每个任务都依赖于最后一个任务,则没有必要使它们异步运行。 也就是说,如果该程序涉及套系列任务,你可以异步运行每套。
步骤1:确定程序的同步和异步部分
Python异步代码必须由Python应用程序的同步部分启动和管理。 为此,将程序转换为异步时的首要任务是在代码的同步和异步部分之间划清界限。
在上一篇关于async的文章中 ,我们使用了一个web scraper应用程序作为简单示例。 代码的异步部分是打开网络连接并从站点读取的例程,即您要插入的所有内容。 但是程序的所有部分都不是异步的。 它启动异步任务,然后在完成任务时将其正常关闭。
同样重要的是,要从异步中分离出任何可能阻止的操作 ,并将其保留在应用程序的同步部分中。 例如,从控制台读取用户输入会阻止一切,包括异步事件循环。 因此,您要在启动异步任务之前或完成异步任务之后处理用户输入。 (它可以处理用户输入通过异步多处理或线程,但是这是一种先进的运动,我们不会进入这里。)
阻止操作的一些示例:
- 控制台输入(如上所述)。
- 涉及大量CPU使用率的任务。
- 使用
time.sleep
强制暂停。 请注意,您可以通过使用异步函数里面睡觉asyncio.sleep
的替代品time.sleep
。
步骤2:将适当的同步功能转换为异步功能
一旦知道了程序的哪些部分将异步运行,就可以将它们划分为函数(如果尚未划分),然后使用async
关键字将其转换为异步函数。 然后,您需要将代码添加到应用程序的同步部分,以运行异步代码并在需要时从中收集结果。
注意:您将需要检查已使每个函数异步的调用链,并确保它们没有调用可能长时间运行或阻塞的操作。 异步函数可以直接调用同步函数,如果该同步函数阻塞,那么异步函数也可以调用它。
让我们看一下如何进行同步到异步转换的简化示例。 这是我们的“之前”程序:
def a_function():
# some async-compatible action that takes a while
def another_function():
# some sync function, but not a blocking one
def do_stuff():
a_function()
another_function()
def main():
for _ in range(3):
do_stuff()
main()
如果我们希望do_stuff
三个实例作为异步任务运行,则需要将do_stuff
(以及它涉及的所有内容)转换为异步代码。 这是转换的第一步:
import asyncio
async def a_function():
# some async-compatible action that takes a while
def another_function():
# some sync function, but not a blocking one
async def do_stuff():
await a_function()
another_function()
async def main():
tasks = []
for _ in range(3):
tasks.append(asyncio.create_task(do_stuff()))
await asyncio.gather(tasks)
asyncio.run(main())
注意我们对main
所做的更改。 现在main
使用asyncio
将do_stuff
每个实例作为并发任务启动,然后等待结果( asyncio.gather
)。 我们还希望将a_function
转换为异步函数,因为我们希望a_function
所有实例a_function
运行,并与需要异步行为的其他函数一起运行。
如果想进一步,我们还可以将another_function
转换为async:
async def another_function():
# some sync function, but not a blocking one
async def do_stuff():
await a_function()
await another_function()
但是,使another_function
异步将是多余的,因为(如我们所指出的)它不会做任何会阻碍程序进度的事情。 另外,如果程序的任何同步部分都称为another_function
,那么我们也必须将它们转换为异步方式,这可能会使我们的程序比需要的复杂。
步骤3:彻底测试您的Python异步程序
任何经过异步转换的程序都必须在投入生产之前进行测试,以确保其按预期工作。
如果您的程序大小适中(例如,大约几十行)并且不需要完整的测试套件,那么验证它是否按预期工作就不难了。 就是说,如果您将程序转换为异步,作为一个更大的项目的一部分(其中测试套件是标准夹具),那么为异步和同步组件编写单元测试是有意义的。
Python中的两个主要测试框架现在都具有某种异步支持。 Python的unittest
框架包括用于异步功能测试案例的对象 ,并pytest
报价pytest-asyncio
用于同样目的。
最后,在为异步组件编写测试时,您需要作为测试的条件来处理它们的异步性。 例如,不能保证异步作业将按提交顺序完成。 第一个可能排在最后,有些可能根本无法完成。 您为异步功能设计的任何测试都必须考虑这些可能性。
如何使用Python做更多的事情
- 开始使用Python进行异步处理
- 如何在Python中使用asyncio
- 如何使用PyInstaller创建Python可执行文件
- Cython教程:如何加速Python
- 如何聪明地安装Python
- 如何使用Poetry管理Python项目
- 如何使用Pipenv管理Python项目
- Virtualenv和Venv:解释了Python虚拟环境
- Python virtualenv和venv做和不做
- Python线程和子流程说明
- 如何使用Python调试器
- 如何使用timeit来分析Python代码
- 如何使用cProfile来分析Python代码
- 如何将Python转换为JavaScript(然后再次转换)
翻译自: https://www.infoworld.com/article/3562577/3-steps-to-a-python-async-overhaul.html
python3异步