news 2026/4/23 9:46:15

python 协程的简单使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
python 协程的简单使用

运行协程,asyncio 提供了三种主要机制

asyncio.run最高层级的入口点

对协程执行 await

使用asyncio.create_task()函数用来并发运行作为 asyncio任务的多个协程

多个顺序执行

importasyncioimporttime asyncio.runasyncdefsay_after(delay,what):awaitasyncio.sleep(delay)print(what)asyncdefmain():print(f"started at{time.strftime('%X')}")awaitsay_after(1,'hello')awaitsay_after(2,'world')print(f"finished at{time.strftime('%X')}")asyncio.run(main())

started at 20:15:44
hello
world
finished at 20:15:47

多个异步执行

importasyncioimporttimeasyncdefsay_after(delay,what):awaitasyncio.sleep(delay)print(what)asyncdefmain():task1=asyncio.create_task(say_after(1,'hello'))task2=asyncio.create_task(say_after(2,'world'))print(f"started at{time.strftime('%X')}")# Wait until both tasks are completed (should take# around 2 seconds.)awaittask1awaittask2print(f"finished at{time.strftime('%X')}")asyncio.run(main())

started at 20:17:33
hello
world
finished at 20:17:35

通过上面的对比,可以看到第二个输出比第一个快了一秒

协程高级接口api

关于协程asyncio.gather(*aws,return_exceptions=False)

并发运行aws序列中的可等待对象
这个也是并行,上面的create_task可也并行。其实gather里面调用了create_task。这个loop是更低级的接口

如果return_exceptionsFalse(默认),所引发的首个异常会立即传播给等待gather()的任务。aws序列中的其他可等待对象不会被取消并将继续运行。
如果return_exceptionsTrue,异常会和成功的结果一样处理,并聚合至结果列表。

这个方法会返回一个列表,用于存储可等待对象,也就是协程的返回结果。而且这个结果是跟任务的执行顺序一样

asyncdefrequest_get(session,url):asyncwithsession.get(url)asresponse:returnf'{url}内容:{awaitresponse.text()}'asyncdefmain():asyncwithaiohttp.ClientSession()assession:urls=["https://example.com/a","https://example.com/b","https://example.com/c"]tasks=[asyncio.wait_for(request_get(session,url),timeout=1)forurlinurls]results=awaitasyncio.gather(*tasks,return_exceptions=True)print(results)asyncio.run(main())

输出结果:

[ ‘https://example.com/a 内容: …’,‘https://example.com/b 内容: …’, ‘https://example.com/c 内容:…’]

当为return_exceptions为False,会直接报错,导致程序失败,所以需要增加异常处理。

当为return_exceptions为true,如果有异常,会存进返回的列表中。

asyncdefrequest_get(session,url):ifurl=='https://example.com/a':awaitasyncio.sleep(3)asyncwithsession.get(url)asresponse:returnf'{url}内容:{awaitresponse.text()}'asyncdefmain():asyncwithaiohttp.ClientSession()assession:urls=["https://example.com/a","https://example.com/b","https://example.com/c"]tasks=[asyncio.wait_for(request_get(session,url),timeout=1)forurlinurls]results=awaitasyncio.gather(*tasks,return_exceptions=True)print(results)foriinresults:print(i)asyncio.run(main())

输出结果:

[TimeoutError(),‘https://example.com/b 内容: …’, ‘https://example.com/c 内容:…’]

https://example.com/b 内容: …
https://example.com/c 内容: …

由上面的输出可以看到,TimeoutError()这个异常存进了结果列表。虽然通过for循环遍历了,但是程序并没有报错,而且没有输出,所以这就需要自己处理错误。for循环可以更改为一下所示:

foriinresults:ifisinstance(i,Exception):print(type(i))print(f"任务失败:{type(i).__name__}")else:print(i)

输出如下:

任务失败:TimeoutError
https://example.com/b 内容: …
https://example.com/c 内容: …

单个任务设置超时时间 asyncio.wait_for(aw,timeout)

aw是一个可等待对象,也就是协程
timeout是超时时间,可以为None,也可以为 float 或 int 型数值表示的等待秒数
如果发生超时,任务将取消并引发asyncio.TimeoutError
返回值是执行协程的返回值

多个任务设置超时时间 asyncio.wait(*aws,timeoutreturn_when=ALL_COMPLETED)

并发地运行
aws是一个可等待对象的可迭代对象。这跟上面的wait_for中的aw是不一样的。
timeout是超时时间,可以为None,也可以为 float 或 int 型数值表示的等待秒数
如果发生超时,任务将取消并引发asyncio.TimeoutError
返回两个 Task/Future 集合:(done, pending)这个其实跟python的线程或者进程是类似的,可查看python 线程与多线程简单使用,他也有wait、as_completed方法。

return_when的可选项如下:

常量描述
FIRST_COMPLETED函数将在任意可等待对象结束或取消时返回。
FIRST_EXCEPTION函数将在任意可等待对象因引发异常而结束时返回。当没有引发任何异常时它就相当于ALL_COMPLETED
ALL_COMPLETED函数将在所有可等待对象结束或取消时返回。

注意:
1、这个wait与wait_for不一样,wait_for在超时后会抛错TimeoutError,但是wait方法不会。如下所示:

importaiohttpimportasyncioimporttimeasyncdefrequest_get(session,url):ifurl=='https://example.com/a':awaitasyncio.sleep(3)asyncwithsession.get(url)asresponse:returnf'{url}内容:{awaitresponse.text()}'asyncdefmain():asyncwithaiohttp.ClientSession()assession:urls=["https://example.com/a","https://example.com/b","https://example.com/c"]tasks=[request_get(session,url)forurlinurls]done,pending=awaitasyncio.wait(tasks,timeout=1)print('....................................')print(done)print('....................................')print(pending)

输出:

1766493515.1018946

{<Task finished name=‘Task-3’ coro=<request_get() done, defined at e:\code…> result=‘…’>,

<Task finished name=‘Task-4’ coro=<request_get() done, defined at e:\code…6> result=‘…’>}

{<Task pending name=‘Task-2’ coro=<request_get() running at e:\code\pycharmcode\test\asyhttp_test\01.py:8> wait_for=>}
1766493516.1151073

可以看出,时间从1766493515.1018946到1766493516.1151073,任务3、4都执行完成了,但是任务2没有完成。所有的输出没有任务error,都是task对象。这就引申出第二个注意事项
2、对未完成的任务做处理
上面的例子中,pending集合里还有任务,且它正在执行,如果我们不管,可能会造成资源消耗,内存泄漏等。
上面的例子简单,如果说有复杂的任务在规定时间内没有完成,然后不做处理,就可能会一直运行,我们也不知道。
我们可以运行task.cancel()方法来取消task对象,Task.cancel()不保证 Task 会被取消。示例如下:

fortaskinpending:task.cancel()try:awaittaskexceptasyncio.CancelledError:print("main(): cancel_me is cancelled now")

多个任务设置超时时间asyncio.``as_completed`(aws, ***,timeout=None)

并发地运行aws可迭代对象中的 可等待对象。返回一个协程的迭代器。 所返回的每个协程可被等待以从剩余的可等待对象的可迭代对象中获得最早的下一个结果。如果在所有 Future 对象完成前发生超时则将引发asyncio.TimeoutError

最早的下一个结果指的是结果与任务顺序不对照。例如三个任务1、2、3,执行后返回自己的数字,as_completed返回的可能是[1,3,2]或者[3,1,2],谁先执行完,先返回谁。wait方法返回的结果只能是[1,2,3]

一点解惑

刚开始看协程,对于什么时间使用await不太理解。写出过下面的代码

asyncdefrequest_get(session,url):asyncwithsession.get(url)asresponse:returnf'{url}内容:{awaitresponse.text()}'asyncdefmain():asyncwithaiohttp.ClientSession()assession:urls=["https://example.com/a","https://example.com/b","https://example.com/c"]tasks=[awaitrequest_get(session,url)forurlinurls]done,pending=awaitasyncio.wait(tasks,timeout=1)

tasks = [await request_get(session, url) for url in urls]我在request_get前面使用了await,其实它的使用方法我在文章开头就写了,asyncio提供了三种主要机制来运行协程,其中就有await
所以按照上面我的错误写法,更改成下面的代码,执行你就会发现,没有执行wait,request_get也会执行。

asyncdefrequest_get(session,url):asyncwithsession.get(url)asresponse:returnf'{url}内容:{awaitresponse.text()}'asyncdefmain():asyncwithaiohttp.ClientSession()assession:urls=["https://example.com/a","https://example.com/b","https://example.com/c"]tasks=[awaitrequest_get(session,url)forurlinurls]# done, pending = await asyncio.wait(tasks, timeout=1) 将这一行删掉
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:52:11

30、深入探索 Git 配置与操作

深入探索 Git 配置与操作 1. 实验操作 在本次实验中,我们将继续探索 SourceTree 和 Eclipse(结合 Git)的使用。具体操作步骤如下: 1. 创建新仓库并添加文件 :在 SourceTree 中从头创建一个新的仓库,并向其中添加一个文件。 2. 分支切换 :在 SourceTree 或 Eclip…

作者头像 李华
网站建设 2026/4/18 14:10:11

B站直播推流终极指南:免费工具一键配置OBS

B站直播推流终极指南&#xff1a;免费工具一键配置OBS 【免费下载链接】bilibili_live_stream_code 用于在准备直播时获取第三方推流码&#xff0c;以便可以绕开哔哩哔哩直播姬&#xff0c;直接在如OBS等软件中进行直播&#xff0c;软件同时提供定义直播分区和标题功能 项目地…

作者头像 李华
网站建设 2026/4/17 23:14:05

Unity Native Gallery 终极指南:5分钟搞定手机相册交互开发

Unity Native Gallery 终极指南&#xff1a;5分钟搞定手机相册交互开发 【免费下载链接】UnityNativeGallery A native Unity plugin to interact with Gallery/Photos on Android & iOS (save and/or load images/videos) 项目地址: https://gitcode.com/gh_mirrors/un/…

作者头像 李华
网站建设 2026/4/21 17:28:00

TFTPD64完整使用指南:Windows网络服务器终极配置教程

TFTPD64完整使用指南&#xff1a;Windows网络服务器终极配置教程 【免费下载链接】tftpd64 The working repository of the famous TFTP server. 项目地址: https://gitcode.com/gh_mirrors/tf/tftpd64 TFTPD64是一款功能强大的Windows网络服务器套件&#xff0c;集成了…

作者头像 李华
网站建设 2026/4/15 20:03:12

MMseqs2蛋白质序列分析中PDB数据库集成的最佳实践

MMseqs2作为超快速、高灵敏度的蛋白质序列搜索与聚类套件&#xff0c;为生物信息学研究提供了强大的工具支持。在实际应用中&#xff0c;PDB数据库的集成是许多用户面临的共同挑战。 【免费下载链接】MMseqs2 MMseqs2: ultra fast and sensitive search and clustering suite …

作者头像 李华
网站建设 2026/4/16 18:25:20

如何快速掌握上海交通大学LaTeX论文模板:新手终极使用指南

如何快速掌握上海交通大学LaTeX论文模板&#xff1a;新手终极使用指南 【免费下载链接】SJTUThesis 上海交通大学 LaTeX 论文模板 | Shanghai Jiao Tong University LaTeX Thesis Template 项目地址: https://gitcode.com/gh_mirrors/sj/SJTUThesis 上海交通大学LaTeX论…

作者头像 李华