如何使用Python中的asyncio?

译文
开发 后端
利用Python的asyncio库中的高级异步函数,编写更高效的Python应用程序。

[[315672]]

【51CTO.com快译】Python的异步编程功能(简称async)让你可以编写不必等待独立任务完成就可以完成更多工作的程序。Python附带的asyncio库为你提供了使用async处理磁盘或网络I/O、无需其他方面等待的工具。

asyncio提供了两种处理异步操作的API:高级和低级。高级API用途广泛,适用于各种应用程序。低级API功能强大,但也很复杂,使用频率较低。

本文重点介绍高级API。我们会逐步介绍asyncio中常用的高级API,说明它们如何可用于涉及异步任务的常见操作。

如果你完全刚接触Python中的async,或者想了解其工作原理,不妨先看看本人介绍Python async的文章:https://www.infoworld.com/article/3454442/get-started-with-async-in-python.html,之后再作深入研究。

在Python中运行协程和任务

很自然,asyncio最常见的用途是运行Python脚本的异步部分。这意味着学会使用协程和任务。

Python的异步组件(包括协程和任务)只能与其他异步组件一起使用,不能与常规的同步Python一起使用,因此需要asyncio来填补缺口。为此,你要使用asyncio.run函数: 

  1. import asyncio  
  2. async def main():  
  3. print ("Waiting 5 seconds. " 
  4. for _ in range(5):  
  5. await asyncio.sleep(1)  
  6. print ("." 
  7. print ("Finished waiting." 
  8. asyncio.run(main()) 

这运行main(),连同main()触发的任何例程,等待结果返回。

通常而言,Python程序应只有一个.run()语句,就像Python程序应只有一个main()函数一样。 如果不小心使用,async可能会使程序的控制流难以阅读。程序的异步代码只有一个入口点可以避免情况变得繁复。

异步函数还可以调度安排成tasks,即包装协程并帮助运行协和的对象。 

  1. async def my_task():  
  2. do_something()  
  3. task = asyncio.create_task(my_task()) 

my_task()随后在事件循环中运行,结果存储在task中。

如果你只有一个任务想要获取结果,可以使用asyncio.wait_for(task)来等待任务完成,然后使用task.result()检索结果。但如果你安排了许多任务要执行,并想要等待所有任务完成,不妨使用asyncio.wait([task1, task2])收集结果。(注意,如果你不希望超过一定长度的时间后运行,可以设置操作的超时时间。)

在Python中管理异步事件循环

asyncio的另一个常见用途是管理异步事件循环。事件循环是运行异步函数和回调的对象。使用asyncio.run()时,它自动创建。你通常希望每个程序仅使用一个异步事件循环,同样以便管理。

如果你在编写服务器等更高级的软件,需要对事件循环拥有较低级别的访问权。为此,你可以“揭开面纱”,直接接触事件循环的内部机制。不过如果是简单的工作,不需要这么做。

在Python中使用streams读写数据

async的最佳使用场景是长时间运行的网络操作,其中应用程序可能阻止等待其他某个资源返回结果。为此,asyncio提供了streams,这是用于执行网络I/O的高级机制。这包括充当网络请求的服务器。

asyncio使用两个类StreamReader和StreamWriter,在高级层面进行网络读写。如果你要从网络读取,可以使用asyncio.open_connection()打开连接。该函数返回StreamReader对象和StreamWriter对象的元组,你要在每个对象上使用.read() 和.write()方法以便通信。

想接收来自远程主机的连接,使用asyncio.start_server()。asyncio.start_server()函数将回调函数client_connected_cb作为参数来接受,只要收到请求就调用该函数。该回调函数将StreamReader和StreamWriter的实例作为参数,那样你就能处理服务器的读/写逻辑。这个例子(https://gist.github.com/ethanfrey/75e58db27095936b9e5e)介绍了一个简单的HTTP服务器使用asyncio驱动的aiohttp库。

在Python中同步任务

异步任务往往独立运行,但有时你希望它们彼此通信。asyncio提供了队列和另外几种在任务之间进行同步的机制:

  • 队列:asyncio队列允许异步函数排列Python对象,以便供其他异步函数使用——比如说,基于行为在不同类型的函数之间分配工作负载。
  • 同步原语:asyncio中的锁、事件、条件和信号其工作方式类似常规的Python锁、事件、条件和信号。

关于所有这些方法要记住的一点是它们不是线程安全的。对于在同一事件循环中运行的异步任务来说这不是问题。但如果你试图与不同事件循环、操作系统线程或进程中的任务共享信息,就需要使用threading模块及其对象来执行此操作。

此外,如果你想跨线程边界启动协程,请使用asyncio.run_coroutine_threadsafe()函数,然后将与它结合使用的事件循环作为参数传递。

在Python中暂停协程

asyncio的另一个常见、但很少讨论的用途是在协程内部等待任意时长。为此你不能使用time.sleep(),否则会阻塞整个程序。而是应使用asyncio.sleep(),它允许其他协程继续运行。

在Python中使用较低级别的async

最后,如果你认为构建的应用程序可能需要asyncio的较低级组件,在开始编程之前先考虑一番:很可能有人已经构建了可以满足你需求的基于async的Python库。

比如说,如果你需要异步DNS查询,不妨查看aiodns库;若是异步SSH会话,则有asyncSSH。通过关键字“async”(以及其他与任务相关的关键字)搜索PyPI,或查看人工筛选的Awesome Asyncio列表(https://github.com/timofurrer/awesome-asyncio),以获取灵感。

原文标题:How to use asyncio in Python,作者:Serdar Yegulalp

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

 

责任编辑:庞桂玉 来源: 51CTO
相关推荐

2017-05-05 08:44:24

PythonAsyncio异步编程

2017-08-02 15:00:12

PythonAsyncio异步编程

2023-07-14 15:10:17

PythonAsyncIO库

2024-01-18 08:37:33

socketasyncio线程

2023-11-06 14:13:51

asyncio开发

2016-09-14 21:17:47

PythonAsyncio游戏

2016-09-22 21:12:14

2016-09-19 21:24:08

PythonAsyncio游戏

2017-09-05 08:08:37

asyncio程序多线程

2014-03-31 10:51:40

pythonasyncio

2022-11-17 07:57:34

2021-08-01 15:26:59

协程Asyncio并发数

2022-11-08 11:49:09

NLP库Python云服务

2017-11-01 07:57:08

Python

2021-07-21 14:29:27

Python编程语言软件开发

2018-10-08 15:35:56

Python异步IO

2010-03-15 17:26:58

Python字典

2010-03-17 10:58:47

Python学习笔记

2023-08-30 08:43:42

asyncioaiohttp

2010-02-03 15:40:37

Python函数
点赞
收藏

51CTO技术栈公众号