30. 协程

news/2024/10/4 22:28:48

1.协程的概念

1.1 定义

进程是操作系统内部运行的程序

线程是进程内部运行的程序

协程是线程内部运行的程序

协程是单线程下的并发,又成微线程,英文名coroutine

1.2 协程的优点

 协程切换的开销更小

GIL锁导致同一时刻只能运行一个线程,一个线程内不会限制协程数,单线程就可以实现并发的效果,最大程度利用CPU

1.3 协程的缺点

协程的本质是单线程下的多任务处理,无法利用多核优势

如果协程发生阻塞,在没有使用异步I/O的情况下,那么整个线程将阻塞,所在线程的其它协程任务都不能运行

2. 协程的操作

2.1 协程函数

加上async关键字的函数就是协程函数

async def f1():...

2.2 协程对象

协程函数调用后的返回值就是协程对象

async def f1():print(111)res = f1()
print(res)

 按一般函数的调用方法调用协程函数不会报错,但是会发出警告

 按一般函数的调用方法调用协程函数,函数内部的代码不会执行,只是会返回一个协程对象。

2.3 asyncio模块

Python 3.4:asyncio 被正式引入标准库,是一个实现异步编程的模块。
Python 3.5:引入了 async 和 await 关键字,这使得协程的定义和使用更加直观和简洁。这些关键字取代了早期版本中的 @asyncio.coroutine 装饰器和 yield from 语法。

2.4 事件循环的概念

事件循环可以类比while循环来理解,在循环周期内运行一些任务,特定的条件下结束循环。

import asyncioloop = asyncio.get_event_loop()

2.5 协程函数的两种调用方法,以解释器3.10版本为例

由协程对象的定义可知,按函数名( )无法调用协程函数,需要协程对象和事件循环配合才能实现

[1]方法一:

import asyncioasync def work():print(666)return 'work函数的返回值'def create_coroutine():obj = work()  # 1.调用协程函数,生成协程对象circle = asyncio.get_event_loop()  # 2.建立一个事件循环circle.run_until_complete(obj)  # 3.将协程对象当作任务提交到事件循环的任务列表中,协程运行完成事件循环停止if __name__ == '__main__':create_coroutine()

在 Python 3.10 中,这个警告是因为在没有活动事件循环的主线程中调用 asyncio.get_event_loop() 时,get_event_loop() 会自动创建一个新的事件循环,这种方式被认为是不推荐的。
为了避免这个警告,可以使用 asyncio.run() 来运行协程,这是从 Python 3.7 开始推荐的做法。asyncio.run() 会自动创建和关闭事件循环,简化了异步代码的编写。

 [2]方法二:

import asyncioasync def work():print(666)return 'work函数的返回值'def create_coroutine():obj = work()  # 1.调用协程函数,生成协程对象asyncio.run(obj)  # 2.调用run函数启动协程对象if __name__ == '__main__':create_coroutine()

方式二的本质和方式一是一样的,内部先建立事件循环,然后运行run_until_complete

需要注意的是,run函数在解释器3.7才加入

2.6 await关键字

await关键字用于等待一个async(异步)函数的结果,使用await可以让程序在等待某个操作完成的同时不阻塞整个事件循环,从而允许其它任务运行,对于I/O密集型的应用特别有用。

await关键字解决了一个协程阻塞而导致整个线程阻塞的问题。

注意事项:

1.使用await必须在async(异步)函数中,不能在非异步函数中使用。

2.await只能用于异步函数返回的协程对象。

3.在异步函数内部,await可以用来等待另一个异步函数的结果,这会暂停当前协程的运行,直到等待的协程完成,然后继续运行。

代码示例

import asyncioasync def f1():print('异步函数运行开始')await asyncio.sleep(1)  # 模拟异步操作,比如网络请求print('异步函数运行结束')return 666async def f2():res = await f1()print(res)asyncio.run(f2())  # 获取事件循环并运行f2()协程

分析:

f1是一个异步函数,使用了await来等待asyncio.sleep(1)。这实际上并不会让程序等待1秒,而是允许事件循环在这1秒内执行其它任务。

f2也是一个异步函数,等待f1的运行结果。

ascyncio.run(f2())启动事件循环并运行f2协程。

 2.7 Task对象

[1]概念

前面的协程代码都只创建了一个任务,即事件循环的列表中只有一个任务对象;如需在程序中创建多个任务对象,需要使用Task。

Task用于并发调度协程,通过asyncio.create(协程对象)的方式创建Task对象,可以让协程加入事件循环中等待被调度执行。

注意事项:

asyncio.create_task( )函数在python3.7中被加入,在之前的版本可以改用底层级的asyncio.ensure_future( )函数。

[2]创建多任务方式一

逐个创建任务

import asyncio# 定义协程功能函数
async def work():print('协程运行开始')await asyncio.sleep(1)  # 模拟异步操作,比如网络请求print('协程运行结束')return 666# 定义产生协程函数
async def create_coroutine():print('产生协程的函数运行开始')task1 = asyncio.create_task(work())  # 将work()得到的协程对象封装到Task对象中,并立即添加到事件循环的任务列表中,等待事件循环task2 = asyncio.create_task(work())  # 将work()得到的协程对象封装到Task对象中,并立即添加到事件循环的任务列表中,等待事件循环
response1 = await task1  # 引用了await之后,task1遇到sleep不会阻塞整个线程response2 = await task2print(response1, response2)print('产生协程的函数运行结束')if __name__ == '__main__':asyncio.run(create_coroutine())

 [3]创建多任务方式二

使用列表生成式生成任务

import asyncio# 定义协程功能函数
async def work(num):print('协程运行开始')await asyncio.sleep(1)  # 模拟异步操作,比如网络请求print('协程运行结束')return num * num# 定义产生协程函数
async def create_coroutine():print('产生协程的函数运行开始')# 将work()得到的协程对象封装到Task对象中,并立即添加到事件循环的任务列表中,等待事件循环task_list = [asyncio.create_task(work(i)) for i in range(1, 4)]# 引用了await之后,task1遇到sleep不会阻塞整个线程# 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中# wait里面要放可迭代对象done, pending = await asyncio.wait(task_list, timeout=None)print(done)print(pending)print('产生协程的函数运行结束')if __name__ == '__main__':asyncio.run(create_coroutine())

 [3]获取协程返回值

在步骤[2]的基础上使用async.gather()获取返回值

import asyncio# 定义协程功能函数
async def work(num):print('协程运行开始')await asyncio.sleep(1)  # 模拟异步操作,比如网络请求print('协程运行结束')return num * 10# 定义产生协程函数
async def create_coroutine():print('产生协程的函数运行开始')# 将work()得到的协程对象封装到Task对象中,并立即添加到事件循环的任务列表中,等待事件循环task_list = [asyncio.create_task(work(i)) for i in range(1, 4)]# 引用了await之后,task1遇到sleep不会阻塞整个线程# 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中# wait里面要放可迭代对象done, pending = await asyncio.wait(task_list, timeout=None)response = await asyncio.gather(*task_list)print(response)print('产生协程的函数运行结束')if __name__ == '__main__':asyncio.run(create_coroutine())

 需要注意的是,以上代码即使不运行 done, pending = await asyncio.wait(task_list, timeout=None),也能获取到返回值。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/67809.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

.net core 安装服务

https://www.jianshu.com/p/e1b3b61f876a使用NSSM 后面的代码演示以Asp.net Core 2.1作为演示,其他.Net Core方式一致。 1、确保.Net Core程序可以正常运行 先把Asp.net Core发布,然后直接运行dotnet命令,确保程序可以运行并访问 2、使用NSSM安装dotnet 下载NSSM,使用命…

vs2015安装包丢失或损坏解决工具 或者不能启动

打开“本地组策略编辑器”(gpedit.msc)。展开“计算机配置”>“管理模板”>“系统”>“Internet 通信管理”,然后选择“Internet 通信设置”。选择“关闭自动根证书更新”>,“禁用”,然后选择“确定”或“应用”。  下载最新的组件版本(备份的) https://lea…

uboot 启动自编写程序的方式

uboot 启动自编写程序的方式 uboot 存在 boot 命令。 自己最初在尝试撰写串口程序时,选择了使用汇编来完成。 在这段时间,自己使用 go 命令来尝试载入程序 先是在 Ubuntu 上搭建 tftp 目录 # /etc/default/tftpd-hpaTFTP_USERNAME="tftp" TFTP_DIRECTORY="/ho…

20240924

[牛半仙的妹子 Tree(tree)](http://ac.robo-maker.cn/d/contest/p/ZY1044?tid=66f28cd11bca2159e88c8fb0) 我们会发现其实牛半仙发癫时就等于将以前的标记清空,从头开始,所以我们可以考虑根号分治,如果两个牛半仙发癫的时间间隔小于 \(\sqrt n\) ,那么我们可以直接暴力枚举两…

『模拟赛』冲刺CSP联训模拟2

『模拟赛记录』冲刺CSP联训模拟2Rank 不重要了A. 挤压 你说的对,期望怎么能算签呢? 一个重要的性质:一个数的平方可以在二进制下表示为 \(\sum_{i,j}\ s_i\ s_j\ 2^{i+j}\),所以就可以分别求每一位对答案的贡献了。 设 \(f_{i,1/0,1/0}\) 表示到第 \(i\) 个数我们枚举的两位…

PbootCms上传图片变模糊、上传图片尺寸受限的解决方案

在使用PbootCMS的过程中,如果上传的图片被压缩变得模糊,通常是因为上传的图片尺寸过大。PbootCMS 默认的上传图片限制宽度为 1920 像素,缩略图的限制大小为 10001000 像素。可以通过调整这些参数来解决这个问题。 解决方案打开 config.php 文件 调整 max_width 和 max_heigh…

ROS基础入门——实操教程

ROS新人可看ROS基础入门——实操教程前言 本教程实操为主,少说书。可供参考的文档中详细的记录了ROS的实操和理论,只是过于详细繁杂了,看得脑壳疼,于是做了这个笔记。Ruby Rose,放在这里相当合理前言:本文初编辑于2024年10月24日 CSDN主页:https://blog.csdn.net/rvdgds…

PbootCMS增加可允许上传文件类型,例如webp、mov等文件格式扩展

在PbootCMS中增加可允许上传的文件类型(例如 webp、mov 等文件格式),需要在多个地方进行配置。以下是详细的步骤: 操作步骤 1. 修改 config.php 文件 首先需要修改 config.php 文件,增加允许上传的文件类型。打开 config.php 文件打开 config.php 文件,通常位于 /config …