from greenlet import greenlet

def test1():
print 12
gr2.switch()
print 34 def test2():
print 56
gr1.switch()
print 78 gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

这里创建了两个greenlet协程对象,gr1和gr2,分别对应于函数test1()和test2()。使用greenlet对象的switch()方法,即可以切换协程。上例中,我们先调用”gr1.switch()”,函数test1()被执行,然后打印出”12″;接着由于”gr2.switch()”被调用,协程切换到函数test2(),打印出”56″;之后”gr1.switch()”又被调用,所以又切换到函数test1()。但注意,由于之前test1()已经执行到第5行,也就是”gr2.switch()”,所以切换回来后会继续往下执行,也就是打印”34″;现在函数test1()退出,同时程序退出。由于再没有”gr2.switch()”来切换至函数test2(),所以程序第11行”print 78″不会被执行。

12
56
34

父子关系

创建协程对象的方法其实有两个参数”greenlet(run=None, parent=None)”。参数”run”就是其要调用的方法,比如上例中的函数test1()和test2();参数”parent”定义了该协程对象的父协程,也就是说,greenlet协程之间是可以有父子关系的。如果不设或设为空,则其父协程就是程序默认的”main”主协程。这个”main”协程不需要用户创建,它所对应的方法就是主程序,而所有用户创建的协程都是其子孙。大家可以把greenlet协程集看作一颗树,树的根节点就是”main”,上例中的”gr1″和”gr2″就是其两个字节点。

在子协程执行完毕后,会自动返回父协程。比如上例中test1()函数退出,代码会返回到主程序。让我们写个更清晰的例子来实验下:

from greenlet import greenlet

def test1():
print 12
gr2.switch()
print 34 def test2():
print 56 gr1 = greenlet(test1)
gr2 = greenlet(test2, gr1)
gr1.switch()
print 78

这里创建greenlet对象”gr2″时,指定了其父协程是”gr1″。所以在函数test2()里,虽然没有”gr1.switch()”代码,但是在其退出后,程序一样回到了函数test1(),并且执行”print 34″。同样,在test1()退出后,代码回到了主程序,并执行”print 78″。所以,最后的输出就是:

12
56
34
78

如果上例中,”gr2″的父协程不是”gr1″而是”main”的话,那test2()运行完毕就会回到主程序并直接打印”78″,这样”print 34″就不会执行。大家可以试一试。

还有一个重要的点,就是协程退出后,就无法再被执行了。如果上例在函数test1()中,再加一句”gr2.switch()”,运行的结果是一样的。因为第二次调用”gr2.switch()”,什么也不会运行。

def test1():
print 12
gr2.switch()
print 34
gr2.switch()

大家可能会感觉到父子协程之间的关系,就像函数调用一样,一个嵌套一个。的确,其实greenlet协程的实现就是使用了栈,其运行的上下文保存在栈中,”main”主协程处于栈底的位置,而当前运行中的协程就在栈顶。这同函数是一样。此外,在任何时候,你都可以使用”greenlet.getcurrent()”,获取当前运行中的协程对象。比如在函数test2()中执行”greenlet.getcurrent()”,其返回就等于”gr2″。

异常

既然协程是存放在栈中,那一个协程要抛出异常,就会先抛到其父协程中,如果所有父协程都不捕获此异常,程序才会退出。我们试下,把上面的例子中函数test2()的代码改为:

def test2():
print 56
raise NameError

程序执行后,我们可以看到Traceback信息:

  File "parent.py", line 14, in
gr1.switch()
File "parent.py", line 5, in test1
gr2.switch()
File "parent.py", line 10, in test2
raise NameError

如果将”gr2″的父协程设为空,Traceback信息就会变为:

 File "parent.py", line 14, in
gr1.switch()
File "parent.py", line 10, in test2
raise NameError

因此,如果”gr2″的父协程是”gr1″的话,异常先回抛到函数test1()的代码”gr2.switch()”处。所以,我们再对函数test1()改动下:

def test1():
print 12
try:
gr2.switch()
except NameError:
print 90
print 34

运行后的结果,如果”gr2″的父协程是”gr1″,则异常被捕获,并打印90。否则,异常会被抛出。以上实验很好的证明了,子协程抛出的异常会根据栈里的顺序,依次抛到父协程里。

有一个异常是特例,不会被抛到父协程中,那就是”greenlet.GreenletExit”,这个异常会让当前协程强制退出。比如,我们将函数test2()改为:

def test2():
print 56
raise greenlet.GreenletExit
print 78

那代码行”print 78″永远不会被执行。但这个异常不会往上抛,所以其父协程还是可以正常运行

另外,我们可以通过greenlet对象的”throw()”方法,手动往一个协程里抛个异常。比如,我们在test1()里调一个throw()方法:

def test1():
print 12
gr2.throw(NameError)
try:
gr2.switch()
except NameError:
print 90
print 34

这样,异常就会被抛出,运行后的Trackback是这样的:

  File "exception.py", line 21, in
gr1.switch()
File "exception.py", line 5, in test1
gr2.throw(NameError)

如果将”gr2.throw(NameError)”放在”try”语句中,那该异常就会被捕获,并打印”90″。另外,当”gr2″的父协程不是”gr1″而是”main”时,异常会直接抛到主程序中,此时函数test1()中的”try”语句就不起作用了。

协程间传递消息

在介绍生成器时,我们聊过可以使用生成器的send()方法来传递参数。greenlet也同样支持,只要在其switch()方法调用时,传入参数即可。我们再来基于本文第一个例子改造下:

from greenlet import greenlet

def test1():
print 12
y = gr2.switch(56)
print y def test2(x):
print x
gr1.switch(34)
print 78 gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

在test1()中调用”gr2.switch()”,由于协程”gr2″之前未被启动,所以传入的参数”56″会被赋在test2()函数的参数”x”上;在test2()中调用”gr1.switch()”,由于协程”gr1″之前已执行到第5行”y = gr2.switch(56)”这里,所以传入的参数”34″会作为”gr2.switch(56)”的返回值,赋给变量”y”。这样,两个协程之间的互传消息就实现了。

生产者消费者的例子,改为greenlet实现吧:

from greenlet import greenlet

def consumer():
last = ''
while True:
receival = pro.switch(last)
if receival is not None:
print 'Consume %s' % receival
last = receival def producer(n):
con.switch()
x = 0
while x < n:
x += 1
print 'Produce %s' % x
last = con.switch(x) pro = greenlet(producer)
con = greenlet(consumer)
pro.switch(5)

用greenlet实现Python中的并发的更多相关文章

  1. Python中的并发编程

    简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执 ...

  2. python中实现并发的手段之 协程

    几种实现并发的手段 进程 启动多个进程 进程之间是由操作系统负责调用线程 启动多个线程 真正被CPU执行的最小单位实际是线程 开启一个线程 创建一个线程 寄存器 堆栈 关闭一个线程协程 本质上是一个线 ...

  3. Python中的并发

    目录 Python并发 并发三种层次 协程 生成者消费者 新关键字 网络io 线/进程 例子 线程池 进程通信 并发池 future对象 executor对象 参考 Python并发 并发三种层次 个 ...

  4. python中的并发执行

    一. Gevent实例 import gevent import requests from gevent import monkey # socket发送请求以后就会进入等待状态,gevent更改了 ...

  5. python中的协程及实现

    1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...

  6. Python中实现异步并发查询数据库

    这周又填了一个以前挖下的坑. 这个博客系统使用Psycopy库实现与PostgreSQL数据库的通信.前期,只是泛泛地了解了一下SQL语言,然后就胡乱拼凑出这么一个简易博客系统. 10月份找到工作以后 ...

  7. python中并发编程基础1

    并发编程基础概念 1.进程. 什么是进程? 正在运行的程序就是进程.程序只是代码. 什么是多道? 多道技术: 1.空间上的复用(内存).将内存分为几个部分,每个部分放入一个程序,这样同一时间在内存中就 ...

  8. python中的协程:greenlet和gevent

    python中的协程:greenlet和gevent 协程是一中多任务实现方式,它不需要多个进程或线程就可以实现多任务. 1.通过yield实现协程: 代码: import time def A(): ...

  9. python web中的并发请求

    python web可以选择django,也可以选择flask,它们的原理差不多.flask比较轻量,下面写一段flask程序来说明python web对并发请求的处理. app.py import ...

随机推荐

  1. 【BZOJ3295】动态逆序对(线段树,树状数组)

    [BZOJ3295]动态逆序对(线段树,树状数组) 题面 Description 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的 ...

  2. Net 面试随想

    佳节已去,至今已半月有余,近来园中唱衰net的声音幽幽而起,net不成熟的大环境一直被作为诟病,net core的跨平台去年抄的火热,是否为net 崛起的最后一根稻草,结合我面试的情况,作为小白,嘟囔 ...

  3. linux同步与通信

    这几天读完了UNP v2,对进程间通信与同步的方式有所了解,现对主要的知识点总结如下: 根据出现的历史,先有的管道,FIFO,信号,然后是systemV IPC,再是后来的Poxis IPC,syst ...

  4. handsontable 方法汇总

    核心方法 1.为handsontable添加钩子方法 addHook(key,callback):key为钩子方法名 例如:hot.addHook('beforeInit', myCallback); ...

  5. docker 使用案例:部署nginx

    首先安装docker.可以参考这篇教程: http://www.runoob.com/docker/windows-docker-install.html 本教程以windows10+ubuntu:1 ...

  6. javascript 原型及原型链详解

    我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个原型对象,而这个原型对象中拥有的属性和方法可以被所以实例共享. function Person(){ } Pe ...

  7. 关于“应用程序无法启动,因为应用程序的并行配置不正确。请参阅应用程序事件日志,或使用命令行sxstrace.exe工具”问题的解决方法

    今天打开QQ管家加速版的时候突然出现了这个错误,百度了下说是系统缺少Microsoft Visual C++ 20XX(运行库),下载这个安装即可解决问题.

  8. shell总的exit与sleep

  9. Java设计模式(五)Prototype原型模式

    一.场景描述 创建型模式中,从工厂方法模式,抽象工厂模式,到建造者模式,再到原型模式,我的理解是,创建对象的方式逐步从编码实现转向内存对象处理. 例如,在“仪器数据采集器”的子类/对象“PDF文件数据 ...

  10. The based of tuning

    调优目的: 1.  提高资源利用率 2.  找出性能瓶颈并缓解 3.  通过性能管理实现合理的资源分配,提升硬件性价比 调优分层及效率问题: 业务级调优  eg:1)网站使用的Apache—>业 ...