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…...

大数据自学学习技巧?
经常有人说:先别管大数据是什么,现在理解不了没关系,先开始学,等学着学着就明白了,这种学习路线基本是混合的,很难分清楚自己学了这段怎么用在以后项目中,所以会越学越迷茫,但是等你…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)
+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...