适合小白的 Asyncio 教程!

2024-04-17 07:08
文章标签 教程 小白 asyncio 适合

本文主要是介绍适合小白的 Asyncio 教程!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方“Python乱炖”,选择“加为星标”

第一时间关注Python技术干货!

所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知。

Asyncio 是并发(concurrency)的一种方式。对 Python 来说,并发还可以通过线程(threading)和多进程(multiprocessing)来实现。

Asyncio 并不能带来真正的并行(parallelism)。当然,因为 GIL(全局解释器锁)的存在,Python 的多线程也不能带来真正的并行。

可交给 asyncio 执行的任务,称为协程(coroutine)。一个协程可以放弃执行,把机会让给其它协程(即 yield fromawait)。

1. 定义协程

协程的定义,需要使用 async def 语句。

async def do_some_work(x): pass

do_some_work 便是一个协程。
准确来说,do_some_work 是一个协程函数,可以通过 asyncio.iscoroutinefunction 来验证:

print(asyncio.iscoroutinefunction(do_some_work)) # True

这个协程什么都没做,我们让它睡眠几秒,以模拟实际的工作量 :

async def do_some_work(x):print("Waiting " + str(x))await asyncio.sleep(x)

在解释 await 之前,有必要说明一下协程可以做哪些事。协程可以:

  • 等待一个 future 结束

  • 等待另一个协程(产生一个结果,或引发一个异常)

  • 产生一个结果给正在等它的协程

  • 引发一个异常给正在等它的协程

asyncio.sleep 也是一个协程,所以 await asyncio.sleep(x) 就是等待另一个协程。可参见 asyncio.sleep 的文档:

sleep(delay, result=None, *, loop=None)
Coroutine that completes after a given time (in seconds).

2. 运行协程

调用协程函数,协程并不会开始运行,只是返回一个协程对象,可以通过 asyncio.iscoroutine 来验证:

print(asyncio.iscoroutine(do_some_work(3))) # True

此处还会引发一条警告:

async1.py:16: RuntimeWarning: coroutine 'do_some_work' was never awaitedprint(asyncio.iscoroutine(do_some_work(3)))

要让这个协程对象运行的话,有两种方式:

  • 在另一个已经运行的协程中用 await 等待它

  • 通过 ensure_future 函数计划它的执行

简单来说,只有 loop 运行了,协程才可能运行。
下面先拿到当前线程缺省的 loop ,然后把协程对象交给 loop.run_until_complete,协程对象随后会在 loop 里得到运行。

loop = asyncio.get_event_loop()
loop.run_until_complete(do_some_work(3))

run_until_complete 是一个阻塞(blocking)调用,直到协程运行结束,它才返回。这一点从函数名不难看出。
run_until_complete 的参数是一个 future,但是我们这里传给它的却是协程对象,之所以能这样,是因为它在内部做了检查,通过 ensure_future 函数把协程对象包装(wrap)成了 future。所以,我们可以写得更明显一些:

loop.run_until_complete(asyncio.ensure_future(do_some_work(3)))

完整代码:

import asyncioasync def do_some_work(x):print("Waiting " + str(x))await asyncio.sleep(x)loop = asyncio.get_event_loop()
loop.run_until_complete(do_some_work(3))

运行结果:

Waiting 3
<三秒钟后程序结束>

3. 回调函数

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

def done_callback(futu):print('Done')futu = asyncio.ensure_future(do_some_work(3))
futu.add_done_callback(done_callback)loop.run_until_complete(futu)

4. 多个协程

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

loop.run_until_complete(asyncio.gather(do_some_work(1), do_some_work(3)))

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

coros = [do_some_work(1), do_some_work(3)]
loop.run_until_complete(asyncio.gather(*coros))

运行结果:

Waiting 3
Waiting 1
<等待三秒钟>
Done

这两个协程是并发运行的,所以等待的时间不是 1 + 3 = 4 秒,而是以耗时较长的那个协程为准。

参考函数 gather 的文档:

gather(*coros_or_futures, loop=None, return_exceptions=False)
Return a future aggregating results from the given coroutines or futures.

发现也可以传 futures 给它:

futus = [asyncio.ensure_future(do_some_work(1)),asyncio.ensure_future(do_some_work(3))]loop.run_until_complete(asyncio.gather(*futus))

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

5. run_until_complete和run_forever

我们一直通过 run_until_complete 来运行 loop ,等到 future 完成,run_until_complete 也就返回了。

async def do_some_work(x):print('Waiting ' + str(x))await asyncio.sleep(x)print('Done')loop = asyncio.get_event_loop()coro = do_some_work(3)
loop.run_until_complete(coro)

输出:

Waiting 3
<等待三秒钟>
Done
<程序退出>

现在改用 run_forever

async def do_some_work(x):print('Waiting ' + str(x))await asyncio.sleep(x)print('Done')loop = asyncio.get_event_loop()coro = do_some_work(3)
asyncio.ensure_future(coro)loop.run_forever()

输出:

Waiting 3
<等待三秒钟>
Done
<程序没有退出>

三秒钟过后,future 结束,但是程序并不会退出。run_forever 会一直运行,直到 stop 被调用,但是你不能像下面这样调 stop

loop.run_forever()
loop.stop()

run_forever 不返回,stop 永远也不会被调用。所以,只能在协程中调 stop

async def do_some_work(loop, x):print('Waiting ' + str(x))await asyncio.sleep(x)print('Done')loop.stop()

这样并非没有问题,假如有多个协程在 loop 里运行:

asyncio.ensure_future(do_some_work(loop, 1))
asyncio.ensure_future(do_some_work(loop, 3))loop.run_forever()

第二个协程没结束,loop 就停止了——被先结束的那个协程给停掉的。
要解决这个问题,可以用 gather 把多个协程合并成一个 future,并添加回调,然后在回调里再去停止 loop。

async def do_some_work(loop, x):print('Waiting ' + str(x))await asyncio.sleep(x)print('Done')def done_callback(loop, futu):loop.stop()loop = asyncio.get_event_loop()futus = asyncio.gather(do_some_work(loop, 1), do_some_work(loop, 3))
futus.add_done_callback(functools.partial(done_callback, loop))loop.run_forever()

其实这基本上就是 run_until_complete 的实现了,run_until_complete 在内部也是调用 run_forever

6. Close Loop?

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

loop.run_until_complete(do_some_work(loop, 1))
loop.run_until_complete(do_some_work(loop, 3))
loop.close()

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

loop.run_until_complete(do_some_work(loop, 1))
loop.close()
loop.run_until_complete(do_some_work(loop, 3))  # 此处异常

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

7. gather 和 wait

asyncio.gatherasyncio.wait 功能相似。

coros = [do_some_work(loop, 1), do_some_work(loop, 3)]
loop.run_until_complete(asyncio.wait(coros))

具体差别可请参见 StackOverflow 的讨论:Asyncio.gather vs asyncio.wait。

8. Timer

C++ Boost.Asio 提供了 IO 对象 timer,但是 Python 并没有原生支持 timer,不过可以用 asyncio.sleep 模拟。

async def timer(x, cb):futu = asyncio.ensure_future(asyncio.sleep(x))futu.add_done_callback(cb)await futut = timer(3, lambda futu: print('Done'))
loop.run_until_complete(t)

原文:https://segmentfault.com/a/1190000008814676

- EOF -

为了大家更快速的学习知识,掌握技术,随时沟通交流问题,特组建了技术交流群,大家在群里可以分享自己的技术栈,抛出日常问题,群里会有很多大佬及时解答的,这样我们就会结识很多志同道合的人,长按下图可加我微信,备注:Python即可进群。扫码加群????

回复:[学习资料]获取最新Python学习资料

这篇关于适合小白的 Asyncio 教程!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/911112

相关文章

全网最全Tomcat完全卸载重装教程小结

《全网最全Tomcat完全卸载重装教程小结》windows系统卸载Tomcat重新通过ZIP方式安装Tomcat,优点是灵活可控,适合开发者自定义配置,手动配置环境变量后,可通过命令行快速启动和管理... 目录一、完全卸载Tomcat1. 停止Tomcat服务2. 通过控制面板卸载3. 手动删除残留文件4.

Python的pandas库基础知识超详细教程

《Python的pandas库基础知识超详细教程》Pandas是Python数据处理核心库,提供Series和DataFrame结构,支持CSV/Excel/SQL等数据源导入及清洗、合并、统计等功能... 目录一、配置环境二、序列和数据表2.1 初始化2.2  获取数值2.3 获取索引2.4 索引取内容2

python依赖管理工具UV的安装和使用教程

《python依赖管理工具UV的安装和使用教程》UV是一个用Rust编写的Python包安装和依赖管理工具,比传统工具(如pip)有着更快、更高效的体验,:本文主要介绍python依赖管理工具UV... 目录前言一、命令安装uv二、手动编译安装2.1在archlinux安装uv的依赖工具2.2从github

C#实现SHP文件读取与地图显示的完整教程

《C#实现SHP文件读取与地图显示的完整教程》在地理信息系统(GIS)开发中,SHP文件是一种常见的矢量数据格式,本文将详细介绍如何使用C#读取SHP文件并实现地图显示功能,包括坐标转换、图形渲染、平... 目录概述功能特点核心代码解析1. 文件读取与初始化2. 坐标转换3. 图形绘制4. 地图交互功能缩放

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

spring AMQP代码生成rabbitmq的exchange and queue教程

《springAMQP代码生成rabbitmq的exchangeandqueue教程》使用SpringAMQP代码直接创建RabbitMQexchange和queue,并确保绑定关系自动成立,简... 目录spring AMQP代码生成rabbitmq的exchange and 编程queue执行结果总结s

Python实现数据可视化图表生成(适合新手入门)

《Python实现数据可视化图表生成(适合新手入门)》在数据科学和数据分析的新时代,高效、直观的数据可视化工具显得尤为重要,下面:本文主要介绍Python实现数据可视化图表生成的相关资料,文中通过... 目录前言为什么需要数据可视化准备工作基本图表绘制折线图柱状图散点图使用Seaborn创建高级图表箱线图热