python中的协程
协程,又称微线程,纤程。英文名Coroutine。
协程是啥
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。
协程和线程差异
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
简单实现协程
import time
def test1():
while True:
print("--test1--")
yield
time.sleep(0.5)
def test2():
while True:
print("--test2--")
yield
time.sleep(0.5)
if __name__ == "__main__":
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)
greenlet
安装方式
sudo pip3 install greenlet
import time
from greenlet import greenlet
def test1():
while True:
print("--test1--")
g2.switch()
time.sleep(0.5)
def test2():
while True:
print("--test2--")
g1.switch()
time.sleep(0.5)
g1 = greenlet(test1)
g2 = greenlet(test2)
g1.switch()
运行效果:
--test1--
--test2--
--test1--
--test2--
--test1--
--test2--
--test1--
--test2--
--test1--
。。。
gevent
greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
安装
pip3 install gevent
1. gevent的使用
import gevent
def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
g1 = gevent.spawn(test, 5)
g2 = gevent.spawn(test, 5)
g3 = gevent.spawn(test, 5)
g1.join()
g2.join()
g3.join()
2. gevent切换执行
import gevent
def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5)
g1 = gevent.spawn(test, 5)
g2 = gevent.spawn(test, 5)
g3 = gevent.spawn(test, 5)
g1.join()
g2.join()
g3.join()
运行结果:
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 0
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 0
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 0
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 1
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 1
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 1
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 2
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 2
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 2
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 3
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 3
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 3
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 4
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 4
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 4
3. 给程序打补丁
import time
import gevent
def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
g1 = gevent.spawn(test, 5)
g2 = gevent.spawn(test, 5)
g3 = gevent.spawn(test, 5)
g1.join()
g2.join()
g3.join()
运行结果:
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 0
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 1
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 2
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 3
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 4
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 0
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 1
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 2
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 3
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 4
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 0
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 1
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 2
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 3
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 4
import time
import gevent
from gevent import monkey
monkey.patch_all() # 讲程序中用到的耗时的代码,换为gevent中实现的代码
def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# g1 = gevent.spawn(test, 5)
# g2 = gevent.spawn(test, 5)
# g3 = gevent.spawn(test, 5)
# g1.join()
# g2.join()
# g3.join()
gevent.joinall([
gevent.spawn(test, 5),
gevent.spawn(test, 5),
gevent.spawn(test, 5)
])
运行结果:
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 0
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 0
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 0
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 1
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 1
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 1
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 2
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 2
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 2
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 3
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 3
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 3
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 4
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 4
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 4
进程、线程、协程对比
请仔细理解如下的通俗描述
- 有一个老板想要开个工厂进行生产某件商品(例如剪子)
- 他需要花一些财力物力制作一条生产线,这个生产线上有很多的器件以及材料这些所有的 为了能够生产剪子而准备的资源称之为:进程
- 只有生产线是不能够进行生产的,所以老板的找个工人来进行生产,这个工人能够利用这些材料最终一步步的将剪子做出来,这个来做事情的工人称之为:线程
- 这个老板为了提高生产率,想到3种办法:
- 在这条生产线上多招些工人,一起来做剪子,这样效率是成倍増长,即单进程 多线程方式
- 老板发现这条生产线上的工人不是越多越好,因为一条生产线的资源以及材料毕竟有限,所以老板又花了些财力物力购置了另外一条生产线,然后再招些工人这样效率又再一步提高了,即多进程 多线程方式
- 老板发现,现在已经有了很多条生产线,并且每条生产线上已经有很多工人了(即程序是多进程的,每个进程中又有多个线程),为了再次提高效率,老板想了个损招,规定:如果某个员工在上班时临时没事或者再等待某些条件(比如等待另一个工人生产完谋道工序 之后他才能再次工作) ,那么这个员工就利用这个时间去做其它的事情,那么也就是说:如果一个线程等待某些条件,可以充分利用这个时间去做其它事情,其实这就是:协程方式
简单总结
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
python中的协程的更多相关文章
- python中的协程及实现
1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...
- python中的协程:greenlet和gevent
python中的协程:greenlet和gevent 协程是一中多任务实现方式,它不需要多个进程或线程就可以实现多任务. 1.通过yield实现协程: 代码: import time def A(): ...
- Python中异步协程的使用方法介绍
1. 前言 在执行一些 IO 密集型任务的时候,程序常常会因为等待 IO 而阻塞.比如在网络爬虫中,如果我们使用 requests 库来进行请求的话,如果网站响应速度过慢,程序一直在等待网站响应,最后 ...
- Python中Paramiko协程方式详解
什么是协程 协程我们可以看做是一种用户空间的线程. 操作系统对齐存在一无所知,需要用户自己去调度. 比如说进程,线程操作系统都是知道它们存在的.协程的话是用户空间的线程,操作系统是不知道的. 为什么要 ...
- 协程及Python中的协程
1 协程 1.1协程的概念 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程.(其实并没有说明白~) 我觉得单说协程,比较抽象,如果对线程有一定了解 ...
- python中多进程+协程的使用以及为什么要用它
前面讲了为什么python里推荐用多进程而不是多线程,但是多进程也有其自己的限制:相比线程更加笨重.切换耗时更长,并且在python的多进程下,进程数量不推荐超过CPU核心数(一个进程只有一个GIL, ...
- Python | 详解Python中的协程,为什么说它的底层是生成器?
今天是Python专题的第26篇文章,我们来聊聊Python当中的协程. 我们曾经在golang关于goroutine的文章当中简单介绍过协程的概念,我们再来简单review一下.协程又称为是微线程, ...
- Python中的协程,为什么说它的底层是生成器?
我们曾经在golang关于goroutine的文章当中简单介绍过 协程 的概念,我们再来简单review一下.协程又称为是微线程,英文名是Coroutine.它和线程一样可以调度,但是不同的是线程的启 ...
- python中的协程并发
python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程.无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态 ...
随机推荐
- 微信 SQLite 数据库修复实践
1.前言 众所周知,微信在后台服务器不保存聊天记录,微信在移动客户端所有的聊天记录都存储在一个 SQLite 数据库中,一旦这个数据库损坏,将会丢失用户多年的聊天记录.而我们监控到现网的损坏率是0.0 ...
- package-lock.json,我们应该了解
原谅我占2017年12月31日一个坑,后续补上
- Android JNI 学习(十):String Operations Api & Other Apis
一.String Operations(字符串操作) 1. NewString jstring NewString(JNIEnv *env, const jchar *unicodeChars, js ...
- GIT学习笔记——常用命令
最近使用使用GIT较多,但命令很容易就忘记了,于是整理下,大多整理与一些文档和他人博客 在当前目录新建建一个纯git代码库 $ git --bare init 在当前目录新建一个Git代码库 $ gi ...
- 关于springmvc的helloworld的压测报告
都说hello world 很简单,应该能承受很大的请求压力,那么到底有多大?你知道吗?如果知道,那咱们就不继续了.如果不知道,我们来看一下! 1. 准备工作,快速建立一个基于springmvc的he ...
- Spark基础-scala学习(五、集合)
集合 scala的集合体系结构 List LinkedList Set 集合的函数式编程 函数式编程综合案例:统计多个文本内的单词总数 scala的集合体系结构 scala中的集合体系主要包括:Ite ...
- Aseprite+Cocos:打包像素画图,导入到cocos里并动起来
前言:Aseprite入门教程 Aseprite入门:第一个gif动图 1.制作像素画: 按照上一次的小球跳动制作过程,先制作一个像素画动画: 若是导出gif动态图,效果如下: ...
- python常用的十进制、16进制之间的转换
一 整数之间的进制转换: hex(16) # 10进制转16进制 oct(8) # 10进制转8进制 bin(8) # 10进制转2进制 二 字符串转整数 int(') # 字符串转换成10进制整数 ...
- 数字音频处理的瑞士军刀sox的音效算法以及用法
SoX可以明确的写出需要的音频处理的效果,可以方便的重复使用,在目前的条件下是一个比较方便使用的项目.不过相信随着Audacity的发展,很有可能在未来可以逐渐替代SoX的功能. 对于SoX主要关心的 ...
- onload事件与ready事件的区别,原生js与jquery的区别
onload事件与ready事件分别是原生js与jquery的入口函数 原生js入口函数写法: window.onload=function(){ } jquery入口函数写法: $(document ...