Python 协程详解,都在这里了

news2024/11/15 10:57:19

什么是协程

协程(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)  # 第五步 输出2
    gr2.switch()  # 第六步 切换 func2
 
 
def func2():
    print(3)  # 第三步:输出3
    gr1.switch()  # 第四步:切换回func1 并执行
    print(4)  # 第七步:输出4
 
 
gr1 = greenlet(func1)
gr2 = greenlet(func2)
 
gr1.switch()  # 第0步,切换func1并执行

运行结果:

在这里插入图片描述

2、yield关键字(Python2.x开始)

###免费领python源码籽料qun:5403 05994
def func1():
    yield 1
    yield from func2()
    yield 2
 
 
def func2():
    yield 3
    yield 4
 
 
f1 = 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 05994
 
import asyncio
import time
 
 
async 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 gevent
 
 
def 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密集型的任务中才会发挥作用。

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

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

相关文章

毕设常用模块之舵机介绍以及使用方法

舵机 舵机是一种位置伺服的驱动器,主要是由外壳、电路板、无核心马达、齿轮与位置检测器所构成。其工作原理是由接收机或者单片机发出信号给舵机,其内部有一个基准电路,产生周期为 20ms,宽度为 1.5ms 的基准信号,将获…

易优cms user 登录注册标签

user 登录注册标签 user 登录注册入口标签 [基础用法] 标签&#xff1a;user 描述&#xff1a;动态显示购物车、登录、注册、退出、会员中心的入口&#xff1b; 用法&#xff1a; {eyou:user typeuserinfo} <div id"{$field.htmlid}"> …

如何用项目管理软件,帮助项目经理监控进度?

项目无论规模大小&#xff0c;都要处理许多任务&#xff0c;管理项目文档&#xff0c;监控任务进度等。 有一个方法可以帮助项目经理在制定计划和项目推进时确保一切保持井井有条。 项目管理软件是最有用的工具之一&#xff0c;通常被用于项目计划、时间管理等&#xff0c;能在…

我国近视眼的人数已经超过了六亿,国老花眼人数超过三亿人

眼镜是一种用于矫正视力问题、改善视力、减轻眼睛疲劳的光学器件&#xff0c;在我们的生活中不可忽略的一部分&#xff0c;那么我国眼镜市场发展情况是怎样了&#xff1f;下面小编通过可视化互动平台对我国眼镜市场的状况进行分析。我国是一个近视眼高发的国家&#xff0c;据统…

【MFA】windows环境下,使用Montreal-Forced-Aligner训练并对齐音频

文章目录一、安装MFA1.安装anaconda2.创建并进入虚拟环境3.安装pyTorch二、训练新的声学模型1.确保数据集的格式正确2.训练声音模型-导出模型和对齐文件3.报错处理1.遇到类似&#xff1a; Command ‘[‘createdb’,–host‘ ’, ‘Librispeech’]’ returned non-zero exit sta…

我一个普通程序员,光靠GitHub打赏就年入70万,

一个国外程序员名叫 Caleb Porzio在网上公开了自己用GitHub打赏年入70万的消息和具体做法。 Caleb Porzio 发推庆祝自己靠 GitHub 打赏&#xff08;GitHub Sponsors&#xff09;赚到了 10 万美元。 GitHub Sponsors是 GitHub 2019 年 5 月份推出的一个功能&#xff0c;允许开发…

SpringBatch简介

参考&#xff1a;https://cloud.tencent.com/developer/article/1456757简介SpringBatch主要是一个轻量级的大数据量的并行处理(批处理)的框架。作用和Hadoop很相似&#xff0c;不过Hadoop是基于重量级的分布式环境(处理巨量数据)&#xff0c;而SpringBatch是基于轻量的应用框架…

mac安装vue脚手架失败及解决方法

大家好&#xff0c;这里是 一口八宝周 &#x1f44f;欢迎来到我的博客 ❤️一起交流学习文章中有需要改进的地方请大佬们多多指点 谢谢 &#x1f64f;最近想学前端的心又开始躁动了&#xff0c;于是说干就干&#xff0c;先搞个vue脚手架谁知道上来就失败了说说我的步骤吧&#…

2017年MathorCup数学建模A题流程工业的智能制造解题全过程文档及程序

2017年第七届MathorCup高校数学建模挑战赛 A题 流程工业的智能制造 原题再现&#xff1a; “中国制造 2025”是我国制造业升级的国家大战略。其技术核心是智能制造&#xff0c;智能化程度相当于“德国工业 4.0”水平。“中国制造 2025”的重点领域既包含重大装备的制造业&…

mybatis小demo讲解(详细demo版)

这篇是mybatis的demo演示版噢&#xff0c;如果要了解理论的可以参考这篇哈mybatis从入门到精通好了&#xff0c;我们开始咯 MyBatis小demo1.简单的mybatis小案例1. 创建项目、准备环境2. mybatis的两种实现方式2.1 映射文件Mapper.xml实现1.简单的mybatis小案例 1. 创建项目、…

ESP8266与手机App通信(STM32)

认识模块 ESP8266是一种低成本的Wi-Fi模块&#xff0c;可用于连接物联网设备&#xff0c;控制器和传感器等。它具有小巧、高度集成和低功耗的特点&#xff0c;因此在物联网应用中被广泛使用。ESP8266模块由Espressif Systems开发&#xff0c;具有单芯片的封装和多种功能&#x…

Linux--磁盘存储管理 分区工具 fdisk 了解

对于磁盘存储&#xff0c;既然要管理&#xff0c;要分区&#xff0c;那必然就少不了要 分区的工具对于分区工具&#xff0c;常用的有三个&#xff0c;fdisk 、gdisk、parted ~&#xff01;&#xff01;本次&#xff0c;先介绍下 fdisk 分区工具 在介绍 fdisk 工具之前&#xff…

【UE4 RTS游戏】05-自定义日期和时间

效果步骤打开项目设置&#xff0c;重新设置玩家状态类为“MyGameState”打开“MyGameState”&#xff0c;点击类设置&#xff0c;选中父类为“GameStateBase”接着创建一些变量&#xff1a;&#xff08;1&#xff09;“TimeUnit”&#xff0c;浮点型&#xff0c;私有&#xff0…

线程同步——管程

管程是允许线程具有互斥、等待&#xff08;堵塞&#xff09;某个条件为false的能力的同步结构。具有通知其他线程他们特定条件已经满足的机制。管程为线程提供了可以暂时放弃独占访问&#xff0c;以便在重新获得独占访问并恢复任务之前等待满足某些条件。 管程有互斥锁以及特定…

趣味三角——第15章——傅里叶定理

第15章 傅里叶定理(Fourier’s Theorem) Fourier, not being noble, could not enter the artillery, although he was a second Newton. (傅立叶出生并不高贵&#xff0c;因此按当时的惯例进不了炮兵部队&#xff0c;虽然他是第二个牛顿。) —Franois Jean Dominique Arag…

Mini Pupper在ROS中的仿真

Mini Pupper在ROS中的仿真 课程概述 在这个课程中&#xff0c;你将会学习Mini Pupper结合Rivz和Gazebo的实例应用。你将使用Rviz可视化机器人模型&#xff0c;并在Gazebo仿真环境中实现Mini Pupper的键盘移动控制&#xff0c;然后带着Mini Pupper在你搭建的虚拟世界中散步。 关…

dorcker与vlu靶场搭建

dorcker与vlu靶场搭建 dorcker安装 以kali linux 为例 安装必要的一些系统工具 apt update apt -y install apt-transport-https ca-certificates curl software-properties-common 添加Docker PGP key curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg …

【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.数组的应…

(蓝桥真题)分果果(动态规划)

题目链接&#xff1a;P8746 [蓝桥杯 2021 省 A] 分果果 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 样例1输入&#xff1a; 5 2 6 1 2 7 9 样例1输出&#xff1a; 0 样例2输入&#xff1a; 5 5 6 1 2 7 9 样例2输出&#xff1a; 2 分析&#xff1a;这道题的状态表…

第十二章 opengl之模型加载(Assimp)

OpenGLAssimp模型加载库构建Assimp网格网格渲染Assimp 我们不太能够对像是房子、汽车或者人形角色这样的复杂形状手工定义所有的顶点、法线和纹理坐标。我们要的是将这些模型(Model)导入(Import)到程序当中。模型通常都由3D艺术家在Blender、3DS Max或者Maya这样的工具中精心制…