python中的协程及实现
1.协程的概念:
协程是一种用户态的轻量级线程。协程拥有自己的寄存器上下文和栈。
协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈。
因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每当程序切换回来时,就进入上一次离开时程序所处的代码段。
综合起来,协程的定义就是:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里保存多个控制流的上下文栈
- 一个协程遇到IO操作自动切换到其它协程
2.yield实现的协程
传统的生产者-消费者模型是一个线程生成消息,一个线程取得消息,能过锁机制控制队列和等待,但一不小心就有可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换加生产者继续生产,效率较高。
代码如下:
import time
def consumer():
"""
使用yield生成一个generator生成器
:return:
"""
r = " "
while True:
# yield接收到变量r,处理之后再把结果返回。函数执行到这一步的时候,函数会停留在这一行上,
#当别的函数执行next()语句或者generator.send()语句来激活这一句,本函数就会
#从yield代码的下一行开始继续执行,直到下一次程序循环到yield这里。
n = yield r
print("[consumer]<-- %s" % n)
time.sleep(1)
r = "ok"
def producer(c):
next(c) #启动调用consumer()函数中的生成器
n = 0
while n < 10:
n += 1
print("[producer]-->%s" % n)
#生产者生产产品,通过c.send()把程序切换到consumer函数执行
cr = c.send(n)
print("[producer] consumer return:%s" % cr)
c.close()
if __name__ == "__main__":
c1 = consumer()
producer(c1)
执行结果:
[producer]--> 1
[consumer]<-- 1
[producer] consumer return:ok
[producer]--> 2
[consumer]<-- 2
[producer] consumer return:ok
[producer]--> 3
[consumer]<-- 3
[producer] consumer return:ok
... #中间省略
[producer]--> 9
[consumer]<-- 9
[producer] consumer return:ok
[producer]--> 10
[consumer]<-- 10
[producer] consumer return:ok
整个流程是由一个线程执行,producer和consumer协作完成任务,所以称为协程,而不是线程中的抢占式多任务。
基于协程的定义,刚才使用yield实现的协程并不算合格的协程。
3.由greenlet模块实现的协程
greenlet机制的主要思想是:生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操作进行恢复主止。可以使用一个调度器循环在一组生成器函数之间协作多个任务。greenlet是python中实现协程的一个模块。
使用方式 :
from greenlet import greenlet
import time
def func1():
print("func1,ok1---->",time.ctime())
gr2.switch() #程序会切换到func2执行
time.sleep(5) #休眠5s
print("func1,ok2---->",time.ctime())
gr2.switch() #程序又会切换到func2执行
def func2():
print("func2,ok1---->",time.ctime())
gr1.switch() #func2执行到这里会切换回func1执行
time.sleep(3) #休眠3s
print("func2,ok2---->",time.ctime())
gr1=greenlet(func1)
gr2=greenlet(func2)
gr1.switch()
程序执行流程:
1.程序先运行func1,打印第一句话。
2.func1运行到gr2.switch()这里时,会切换到func2执行,func2函数打印第一句话。
3.func2执行到gr1.switch()这里时,又切换回func1函数的time.sleep(5)执行,func1函数会休眠5s。
4.func1先打印第二句话,执行到gr2.switch()这一句时,再次切换回func2函数。
5.func2函数休眠3s,打印func2函数的第二句话,程序执行完毕。
程序执行结果:
func1,ok1----> Fri Jul 21 16:27:11 2017
func2,ok1----> Fri Jul 21 16:27:11 2017
func1,ok2----> Fri Jul 21 16:27:16 2017
func2,ok2----> Fri Jul 21 16:27:19 2017
4.基于greenlet框架,gevent模块实现协程
python通过yield提供了对协程的基本支持,但是不完全。第三方的gevent模块提供了协程支持。
gevent是第三方库,通过greenlet实现协程。
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent自动切换协程,就保证总有greenlet在运行,而不是等待IO。
代码如下:
import gevent,time
def func1():
print("running in func1--",time.ctime())
time.sleep(2)
print("running in func1 again--",time.ctime())
def func2():
print("running in func2--",time.ctime())
time.sleep(2)
print("running in func2 again--",time.ctime())
t1=time.time()
g1=gevent.spawn(func1)
g2=gevent.spawn(func2)
gevent.joinall([g1,g2])
t2=time.time()
print("cost time:",t2-t1)
程序执行结果:
running in func1-- Fri Jul 21 17:20:17 2017
running in func1 again-- Fri Jul 21 17:20:19 2017
running in func2-- Fri Jul 21 17:20:19 2017
running in func2 again-- Fri Jul 21 17:20:21 2017
cost time: 4.007229328155518
可以看到程序是按顺序执行的。修改程序,使用gevent.sleep()使程序按协程方式执行。
修改后的代码如下:
import gevent,time
def func1():
print("running in func1--",time.ctime())
gevent.sleep(2)
print("running in func1 again--",time.ctime())
def func2():
print("running in func2--",time.ctime())
gevent.sleep(2)
print("running in func2 again--",time.ctime())
t1=time.time()
g1=gevent.spawn(func1)
g2=gevent.spawn(func2)
gevent.joinall([g1,g2])
t2=time.time()
print("cost time:",t2-t1)
程序执行结果:
running in func1-- Fri Jul 21 17:17:00 2017
running in func2-- Fri Jul 21 17:17:00 2017
running in func1 again-- Fri Jul 21 17:17:02 2017
running in func2 again-- Fri Jul 21 17:17:02 2017
cost time: 2.0051145553588867
这样,程序会先执行func1接着执行的是func2,再切换回func1执行。
这种方式可以使原本需要4s才能执行完成的程序只需要执行2s就可以了。
gevent.spawn()方法spawn一些任务,然后通过gevent.joinall将任务加入协程执行队列中等待执行。
5.协程的优点:
无需线程上下文切换造成的资源的浪费。
无需原子操作锁定及同步的开销。
方便切换控制流,简化编程模型。
高并发及高扩展性加低成本:一个CPU支持上万的协程都可以,于高并发处理。
6.协程的缺点:
无法利用多核资源,协程的本质是单个线程,不能同时使用多核CPU。
协程需要与进程配合才能运行在多CPU上。
程序一旦阻塞,会阻塞整个代码段。
python中的协程及实现的更多相关文章
- 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中的协程
目录 协程是啥 协程和线程差异 简单实现协程 greenlet 安装方式 gevent 安装 1. gevent的使用 2. gevent切换执行 3. 给程序打补丁 进程.线程.协程对比 请仔细理解 ...
- python中的协程并发
python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程.无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态 ...
随机推荐
- SPATRA的使用
SPATRA是kali里的集成工具,可以自动化渗透测试 在命令行里键入:sparta即可进入 进去后单机左边空白处 输入你要进行渗透测试的IP 正在扫描中 点击bute可以进入hydra
- RAC+ASM 添加控制文件
环境介绍:11g RAC 使用 ASM 磁盘组,控制文件只有一个,需要进行添加 参照 MOS 文档 How to Multiplex Control File In RAC Database (文档 ...
- bzoj:1700: [Usaco2007 Jan]Problem Solving 解题
Description 过去的日子里,农夫John的牛没有任何题目. 可是现在他们有题目,有很多的题目. 精确地说,他们有P (1 <= P <= 300) 道题目要做. 他们还离开了农场 ...
- C/C++中peek函数的原理及应用
C++中的peek函数 该调用形式为cin.peek() 其返回值是一个char型的字符,其返回值是指针指向的当前字符,但它只是观测,指针仍停留在当前位置,并不后移.如果要访问的字符是文件结束符,则函 ...
- 最小生成树—prim算法
最小生成树prim算法实现 所谓生成树,就是n个点之间连成n-1条边的图形.而最小生成树,就是权值(两点间直线的值)之和的最小值. 首先,要用二维数组记录点和权值.如上图所示无向图: int map[ ...
- ElasticSearch + xpack 使用.md
ElasticSearch 是一个高可用开源全文检索和分析组件.提供存储服务,搜索服务,大数据准实时分析等.一般用于提供一些提供复杂搜索的应.我们为什么要选择 ElasticSearch ?因为它是一 ...
- .NET MongoDB Driver 2.2 API注释
主要内容 1 MongoClient 1.1构造函数 1.2 方法 2 IMongoDatabase 3 IMongoCollection 4 IMongoCollectionExtensions 5 ...
- TI-RTOS 之 GPIO中断(按键)
TI-RTOS 之 GPIO中断(按键) 前面已经用过LED, 定时器,这次来了解GPIO的中断是怎么用的,从CC1310+TI-RTOS的例程可以直接找到相应的例子程序,它的关键是在于要使能中断,也 ...
- Unity Object Pool
using System.Collections; using System.Collections.Generic; using UnityEngine; [System.Serializable] ...
- TCP长连接和短连接的区别
当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需 ...