Python 协程详解,都在这里了
什么是协程
协程(co-routine,又称微线程、纤程)
是一种多方协同的工作方式。
协程不是进程或线程,
其执行过程类似于 Python 函数调用,
Python 的 asyncio 模块实现的异步IO编程框架中,
协程是对使用 async 关键字定义的异步函数的调用。
当前执行者在某个时刻主动让出(yield)控制流,
并记住自身当前的状态,
以便在控制流返回时能从上次让出的位置恢复(resume)执行。
一个进程包含多个线程,
类似于一个人体组织有多种细胞在工作,
同样,一个程序可以包含多个协程。
多个线程相对独立,
线程的切换受系统控制。
同样,多个协程也相对独立,
但是其切换由程序自己控制。
简而言之,
协程的核心思想就在于执行者对控制流的 “主动让出” 和 “恢复”。
相对于,
线程此类的 “抢占式调度” 而言,
协程是一种 “协作式调度” 方式,
协程之间执行任务按照一定顺序交替执行。

Python 对协程的支持经历了多个版本:
- Python2.x 对协程的支持比较有限,通过 yield 关键字支持的生成器实现了一部分协程的功能但不完全。
- 第三方库 gevent 对协程有更好的支持。
- Python3.4 中提供了 asyncio 模块。
- Python3.5 中引入了 async/await 关键字。
- Python3.6 中 asyncio 模块更加完善和稳定。
- Python3.7 中内置了 async/await 关键字。
gevent 是对greenlet进行的封装,
而greenlet 又是对yield进行封装。
一、协程实现方法:
1、greenlet,早期模块
greenlet包是一个Stackless(无栈化的)CPython版本,支持微线程(tasklet)。tasklet可以伪并行的运行并且同步的在信道上交换数据
①首先要先安装greenlet模块
pip install greenlet
from greenlet import greenlet
###免费领python源码籽料qun:5403 05994
def func1():print(1) # 第1步 输出1# 该方法遇到阻塞可以切换到函数2中进行使用gr2.switch() # 第2步:切换到func2中 并执行print(2) # 第五步 输出2gr2.switch() # 第六步 切换 func2def func2():print(3) # 第三步:输出3gr1.switch() # 第四步:切换回func1 并执行print(4) # 第七步:输出4gr1 = greenlet(func1)
gr2 = greenlet(func2)gr1.switch() # 第0步,切换func1并执行
运行结果:

2、yield关键字(Python2.x开始)
###免费领python源码籽料qun:5403 05994
def func1():yield 1yield from func2()yield 2def func2():yield 3yield 4f1 = func1()
for item in f1:print(item)
运行结果:

这里可以思考对比一下yield和return
3、asyncio装饰器(Python 3.4开始)
###免费领python源码籽料qun:5403 05994
# asyncio(在python3.4之后的版本)
# 遇到IO等耗时操作会自动切换
import asyncio
import time@asyncio.coroutine
def func1():print(1)yield from asyncio.sleep(3) # 遇到耗时后会自动切换到其他函数中执行print(2)@asyncio.coroutine
def func2():print(3)yield from asyncio.sleep(2)print(4)@asyncio.coroutine
def func3():print(5)yield from asyncio.sleep(2)print(6)tasks = [asyncio.ensure_future(func1()),asyncio.ensure_future(func2()),asyncio.ensure_future(func3())
]# 协程函数使用 func1()这种方式是执行不了的
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# loop.run_until_complete(func1()) 执行一个函数
end = time.time()
print(end - start) # 只会等待3秒
运行结果:

4、async、await关键字(Python 3.5开始)
###免费领python源码籽料qun:5403 05994import asyncio
import timeasync def func1():print(1)await asyncio.sleep(3) # 遇到耗时后会自动切换到其他函数中执行print(2)async def func2():print(3)await asyncio.sleep(2)print(4)async def func3():print(5)await asyncio.sleep(2)print(6)tasks = [asyncio.ensure_future(func1()),asyncio.ensure_future(func2()),asyncio.ensure_future(func3())
]# 协程函数使用 func1()这种方式是执行不了的
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# loop.run_until_complete(func1()) 执行一个函数
end = time.time()
print(end - start) # 只会等待3秒
运行结果:

5、gevent
import geventdef f1():for i in range(1, 6):print('f1', i)gevent.sleep(0)def f2():for i in range(6, 11):print('f2', i)gevent.sleep(0)t1 = gevent.spawn(f1)
t2 = gevent.spawn(f2)
gevent.joinall([t1, t2])
运行结果:

gevent的优势不仅仅是在代码中调用方便,
厉害的是它拥有的monkey机制。
假设你不愿意修改原来已经写好的python代码,
但是又想充分利用gevent机制,
那么你就可以用monkey来做到这一点。
你所要做的就是在文件开头打一个patch,
那么它就会自动替换你原来的thread、socket、time、multiprocessing等代码,
全部变成gevent框架。
这一切都是由gevent自动完成的。
注意这个patch是在所有module都import了之后再打,
否则没有效果。
甚至在编写的Web App代码的时候,
不需要引入gevent的包,
也不需要改任何代码,
仅仅在部署的时候,
用一个支持gevent的WSGI服务器,
就可以获得数倍的性能提升。
二、协程的运行原理
当程序运行时,
操作系统会为每个程序分配一块同等大小的虚拟内存空间,
并将程序的代码和所有静态数据加载到其中。
然后,创建和初始化 Stack 存储,
用于储存程序的局部变量,
函数参数和返回地址;
创建和初始化 Heap 内存;
创建和初始化 I/O 相关的任务。
当前期准备工作完成后,
操作系统将 CPU 的控制权移交给新创建的进程,
进程开始运行。

一个进程可以有一个或多个线程,
同一进程中的多个线程将共享该进程中的全部系统资源,
如:虚拟地址空间,文件描述符和信号处理等等。
但同一进程中的多个线程有各自的调用栈和线程本地存储。

协程是一种比线程更加轻量级的存在,
协程不是被操作系统内核所管理,
而完全是由用户态程序所控制。
协程与线程以及进程的关系如下图所示。
可见,协程自身无法利用多核,
需要配合进程来使用才可以在多核平台上发挥作用。

协程之间的切换不需要涉及任何 System Call(系统调用)或任何阻塞调用。
协程只在一个线程中执行,切换由用户态控制,而线程的阻塞状态是由操作系统内核来完成的,因此协程相比线程节省线程创建和切换的开销。
协程中不存在同时写变量的冲突,因此,也就不需要用来守卫关键区块的同步性原语,比如:互斥锁、信号量等,并且不需要来自操作系统的支持。
三、协程应用场景
1、抢占式调度的缺点
在 I/O 密集型场景中,
抢占式调度的解决方案是 “异步 + 回调” 机制。

其存在的问题是,
在某些场景中会使得整个程序的可读性非常差。
以图片下载为例,
图片服务中台提供了异步接口,
发起者请求之后立即返回,
图片服务此时给了发起者一个唯一标识 ID,
等图片服务完成下载后把结果放到一个消息队列,
此时需要发起者不断消费这个 MQ 才能拿到下载是否完成的结果。

可见,
整体的逻辑被拆分为了好几个部分,
各个子部分都会存在状态的迁移,
日后必然是 BUG 的高发地。

2、用户态协同调度的优势
而随着网络技术的发展和高并发要求,
协程所能够提供的用户态协同调度机制的优势,
在网络操作、文件操作、
数据库操作、消息队列操作等
重 I/O 操作场景中逐渐被挖掘。

协程将 I/O 的处理权从内核态的操作系统交还给用户态的程序自身。用户态程序在执行 I/O 时,主动的通过 yield(让出)CPU 的执行权给其他协程,多个协程之间处于平等、对称、合作的关系。
四、协程使用注意事项
协程只有和异步IO结合起来才能发挥出最大的威力
假设协程运行在线程之上,
并且协程调用了一个阻塞IO操作,这时候会发生什么?
实际上操作系统并不知道协程的存在,
它只知道线程,
因此在协程调用阻塞IO操作的时候,
操作系统会让线程进入阻塞状态,
当前的协程和其它绑定在该线程之上的协程都会陷入阻塞而得不到调度。
因此,
在协程中尽量不要调用阻塞IO的方法,
比如打印,读取文件,Socket接口等,
除非改为异步调用的方式,
并且协程只有在IO密集型的任务中才会发挥作用。
相关文章:
Python 协程详解,都在这里了
什么是协程 协程(co-routine,又称微线程、纤程) 是一种多方协同的工作方式。 协程不是进程或线程, 其执行过程类似于 Python 函数调用, Python 的 asyncio 模块实现的异步IO编程框架中, 协程是对使用 asy…...
百家号如何写文章赚钱,百家号写文章真的赚钱?
随着互联网的快速发展,越来越多的人开始关注到写文章赚钱这个领域。而在众多写作平台中,头条号无疑是最受欢迎的一个。那么,百家号写文章赚钱是真的吗?如何写文章赚钱呢?下面我们就来一一解答。 首先,百家号…...
【HDFS】datanodeReport RPC优化
cat datanodeReport.txt | awk ‘{print $8}’ | sort | uniq | wc -l 结果15,说明我们有15个router。 每15秒一个router8次调用这个rpc。15秒是我们的监控采集间隔。 看下router为什么要调用这个rpc。 顺着这个配置项去寻找:dfs.federation.router.dn-report.time-out 一…...
【数据结构】研究链表带环问题
💯💯💯💯 本篇主要研究的是链表带环问题,快慢指针的应用,分析不同解法对带环链表的处理,梳理完本篇你将对链表的理解更加透彻Ⅰ.研究链表带环问题Ⅱ.扩展带环问题1.为什么慢指针和快指针一定会相…...
数据仓库的设计思想
数据仓库设计 知识点01:设计大纲与学习目标 #内容大纲1、数据仓库基础知识(回顾)什么是数仓为什么有数仓数仓的特点是什么OLTP和OLAP系统区别(数据库和数仓的区别)2、数仓系统的架构与核心流程核心1:ETL核…...
【JavaSE】数组的定义与使用详解
目录 1.数组的基本概念 1.1数组的好处 1.2什么是数组 1.3数组的定义及初始化 1.3.1数组的创建 1.3.2数组的初始化 1.4数组的使用 1.4.1访问数组中的元素 1.4.2遍历数组 2.数组的类型 2.1认识JVM的内存分布 2.2基本类型变量与引用类型变量 2.3认识null 3.数组的应…...
Kubernetes14:Helm为了部署像微服务这种的大型项目
Kubernetes14:Helm介绍(为了部署像微服务这种的大型项目) 1、Helm的引入 (1)之前方式部署应用基本过程 编写yaml文件 1、deployment kubectl create deployment nginx --imagenginx --dryrun -o yaml > nginx.yaml2、Service kubect…...
2.3操作系统-存储管理:页式存储、逻辑地址、物理地址、物理地址逻辑地址之间的地址关系、页面大小与页内地址长度的关系、缺页中断、内存淘汰规则
2.3操作系统-存储管理:页式存储、逻辑地址、物理地址、物理地址逻辑地址之间的地址关系、页面大小与页内地址长度的关系、缺页中断、内存淘汰规则页式存储逻辑地址、物理地址如何判断物理地址和逻辑地址它们之间的地址关系?页面大小与页内地址长度的关系…...
设计模式3——结构型模式
结构型模式描述如何将类或对象按某种布局组成更大的结构,它分为类结构型和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”&…...
css——图片缩放,拉伸,变形的解决办法
你的图片即将变得超级丝滑图片为什么会拉伸变形?怎么解决?css的object-fit属性object-fit属性有什么用介绍一下object-position举个小栗子图片为什么会拉伸变形? 前端布局时,图片会出现拉伸、缩放和变形的原因可能有多种: 1.例如图…...
【工具使用】STM32CubeMX-基础使用篇
一、概述 无论是新手还是大佬,基于STM32单片机的开发,使用STM32CubeMX都是可以极大提升开发效率的,并且其界面化的开发,也大大降低了新手对STM32单片机的开发门槛。 本文主要面向初次接触STM32CubeMX的同学,大…...
面试题解-理解cookie、session和token
项目vuespringboot 1、token 用户填写密码账号发送至后端,由后端生成token,返回给前端,前端把它存放起来,如放在cookie或者localStorage里面 前端向服务器发起请求时在请求头携带token,判断用户身份给与反应。 //后…...
Buuctf [GUET-CTF2019]number_game 题解
目录 一.主函数逻辑 二.level_stor()函数 三.mid_stor函数 四.operate函数 五.judge2函数 六.求解flag 一.主函数逻辑 ①先输入一个字符串,然后judge1()函数遍历它,判断字符是否在[0,4]区间范围内 ②将输入的字符串用层次遍历的方式存储为一个二叉树root ③再将二叉树r…...
OsgEarth配置.earth文件支持wms服务
<!-- 参考 http://vmap0.tiles.osgeo.org/wms/vmap0?LAYERSbasic&SERVICEWMS&VERSION1.1.1&REQUESTGetMap&STYLES&FORMATimage%2Fjpeg&SRSEPSG%3A4326&BBOX-90,45,-45,90&WIDTH256&HEIGHT256 --> <!-- 可用 2023.03.09--> …...
【数据结构】详解空间复杂度
Yan英杰的博客 悟已往之不谏 知来者之可追 目录 空间复杂度 案例1:计算BubbleSort的空间复杂度? 案例2:计算斐波那契额数列的前N项的空间复杂度 案例3:计算阶乘递归Fac的空间复杂度? 案例4:F1和F2两函数是否使用的同一块空间 案例5:计算该…...
腾讯云GPU游戏服务器/云主机租用配置价格表
用于游戏业务的服务器和普通云服务器和主机空间是不同的,游戏服务器对于硬件的配置、网络带宽有更大的要求,一般游戏服务器根据不同的配置和适用场景会有十几元一小时到几十元一小时,而且可以根据不同的按量计费。而普通的云服务器可能需要几…...
配置临时SSL子域名泛化证书
配置临时SSL子域名泛化证书 三个月有效期第一步:访问SSL证书地址第二步:在华为云上/其他服务器上搜索DNS云解析服务类似的功能第三步:将SSL申请的信息添加到服务器的记录集中第四步:添加完信息进行保存获取key / crt第五步&#x…...
【Linux:环境变量的理解】
目录 1 Z(zombie)-僵尸进程 2 孤儿进程 3 环境变量 3.1 基本概念 3.2 测试HOME 3.3 和环境变量相关的命令 3.4 环境变量的组织方式 3.5 环境变量通常是具有全局属性的 在讲环境变量之前,我们先把上次遗留知识点给总结了(僵尸进程和孤儿进程&…...
python数据类型与数据结构
目录 一、数据类型 1.1变量与常量 1.1.1变量 1.1.2常量 1.2字符串类型 1.3整数与浮点数 1.4List列表 1.5 元组tuple 1.6字典dict 二、字符串格式化 三、数据输入和类型转换 四、简单列表习题练习 一、数据类型 变量类型: 整数int(4字节&#x…...
大数据自学学习技巧?
经常有人说:先别管大数据是什么,现在理解不了没关系,先开始学,等学着学着就明白了,这种学习路线基本是混合的,很难分清楚自己学了这段怎么用在以后项目中,所以会越学越迷茫,但是等你…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
