greenlet微线程
Greenlet简介
一个 “greenlet” 是一个很小的独立微线程。可以把它想像成一个堆栈帧,栈底是初始调用,而栈顶是当前greenlet的暂停位置。你使用greenlet创建一堆这样的堆 栈,然后在他们之间跳转执行。跳转不是绝对的:一个greenlet必须选择跳转到选择好的另一个greenlet,这会让前一个挂起,而后一个恢复。两 个greenlet之间的跳转称为 切换(switch) 。
当你创建一个greenlet,它得到一个初始化过的空堆栈;当你第一次切换到它,他会启动指定的函数,然后切换跳出greenlet。当最终栈底 函数结束时,greenlet的堆栈又编程空的了,而greenlet也就死掉了。greenlet也会因为一个未捕捉的异常死掉。
greenlet又叫协程,是一个伪线程,实际上是串行执行的,其实greenlet不是一种真正的并发机制,而是在同一线程内,在不同函数的执行代码块之间切换,实施“你运行一会、我运行一会”,并且在进行切换时必须指定何时切换以及切换到哪。greenlet的接口是比较简单易用的,但是使用greenlet时的思考方式与其他并发方案存在一定区别。线程/进程模型在大逻辑上通常从并发角度开始考虑,把能够并行处理的并且值得并行处理的任务分离出来,在不同的线程/进程下运行,然后考虑分离过程可能造成哪些互斥、冲突问题,将互斥的资源加锁保护来保证并发处理的正确性。greenlet则是要求从避免阻塞的角度来进行开发,当出现阻塞时,就显式切换到另一段没有被阻塞的代码段执行,直到原先的阻塞状况消失以后,再人工切换回原来的代码段继续处理。因此,greenlet本质是一种合理安排了的串行,greenlet能够得到比较好的性能表现,主要也是因为通过合理的代码执行流程切换,完全避免了死锁和阻塞等情况。因为greenlet本质是串行,因此在没有进行显式切换时,代码的其他部分是无法被执行到的,如果要避免代码长时间占用运算资源造成程序假死,那么还是要将greenlet与线程/进程机制结合使用(每个线程、进程下都可以建立多个greenlet,但是跨线程/进程时greenlet之间无法切换或通讯)。
from greenlet import greenlet def test1():
print 12
gr2.switch()
print 34
gr2.switch() def test2():
print 56
gr1.switch()
print 78 gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
上面是一个greenlet的例子 ,有助于我们理解greenlet的概念 。
输出为 12 56 34 ,
Greenlet属性和操作
理解上面的程序先要学习greenlet的操作有哪些:
greenlet(run=None,parent=None): 创建一个greenlet对象,而不执行。run是回调函数,而parent是父greenlet,缺省是当前greenlet
greenlet.getcurrent():获取当前greenlet,也就是谁在调用这个函数。
greenlet.GreenletExit:用户杀死一个greenlet,这个异常不会波及到父greenlet。
注意上面的greenlet是指库 greenlet
下面是greenlet对象的方法和属性:
g.switch(*args, **kwargs) : 切换执行点到 greenlet对象 g 上。
g.run 调用g,并启动。在g启动后,这个属性就不存在了。
g.parent g的父greenlet。可写的,但是不允许创建循环的父关系。
g.gr_frame 当前帧,或者None
g.dead 判断g是否已经死掉了。
bool(g) 如果g是活跃的返回True,如果尚未启动或者结束后返回False。
g.throw([typ,[val,[tb]]])切换执行点到 g,但是立即抛出指定的异常到g。如果没有提供参数,异常缺省就是greenlet.GreenletExit。如上面描述的,就是异常波及规则。注意调用这个方法等同于如下:
def raiser():
raise typ,val,tb g_raiser = greenlet(raiser,parent=g)
g_raiser.switch()
关于切换 ,switch()方法是greenlet重要的组成部分。switch()用于greenlet之间的切换,当方法被调用时,这会让执行点跳转到greenlet的switch()被调用处。或者在greenlet死掉时,跳转到父greenlet那里去。在切换时,一个对象或者异常被发送到目标greenlet。例如 :
from greenlet import greenlet def test1(x,y):
z= gr2.switch(x+y)
print "z",z
print "test1" def test2(u):
print "u",u
gr1.switch(42,20)
print 'test2' gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch("hello"," world")
结果:
u hello world
z (42, 20)
test1
test2
如果一个greenlet的run()结束了,他会返回值到父greenlet.如果run()是因异常而终止的,异常会波及到父 greenlet(除非是greenlet.GreenletExit异常,这种情况下,异常会被捕捉并返回到父greenlet)。目标greenlet会接收到发送来的对象作为 switch() 的返回值。虽然 switch() 并不会立即返回,但是它仍然会在未来某一点上返回,当其他greenlet切换回来时。当这发生时,执行点恢复到 switch() 之后,而 switch() 返回刚才调用者发送来的对象。这意味着 x=g.switch(y) 会发送对象y到g,然后等着一个不知道是谁发来的对象,并在这里返回给x。
注意上面的z= gr2.switch(x+y),这儿就是把x = “hello” y=“ world”发送到 gr2,然后在test2中 gr1.switch(40,20)返回给 z,所以输出的z 是(40,20).这儿还是比较绕的。
注意,任何切换到死掉的greenlet的行为都会切换到greenlet的父greenlet,或者父的父 ,等等。最终的父是永远不会死掉的”main“ greenlet。
Greenlet与Python线程
greenlet可以和线程一起使用,在这种情况下,每个线程包含一个独立的main greenlet,并拥有自己的greenlet子树。但是不同的线程之间不能切换greenlet。我觉得提高性能的方法就是“多进程+多线程+协程”这里面是一层包含一层的概念。对于python而言,由于GIL的限制,多线程是无法使用多核CPU的。
Greenlet的垃圾回收机制
如果不再有对greenlet对象的引用时(包括其他greenlet的parent),然后没有办法切换回greenlet。在这种情况下会生成一个 GreenletExit 异常到greenlet。这是greenlet异步接收异常的唯一情况。这给出一个 try .. finally 用于清理greenlet内的资源。这个功能同时允许greenlet中无限循环等待数据和处理数据的编程风格。这样循环可以在最后一个引用消失时自动中断。
如果希望greenlet死掉或者通过一个新的引用复活,只需要捕捉和忽略 GreenletExit 异常即可达到无限循环。(这句话我的理解是,捕捉GreenletExit就会导致greenlet死掉,忽略会导致greenlet复活)
greenlet不参与垃圾收集;greenlet帧的循环引用数据会被检测到。将引用传递到其他的循环greenlet会引起内存泄露。
C API 引用
Greenlet可以由C和C++编写的扩展模块或者嵌入python 的程序进行创建和操作。greenlet.h头提供了可供纯python模块调用的全部的API。
Types
Type name | Python name |
---|---|
PyGreenlet | greenlet.greenlet |
Exceptions
Type name | Python name |
---|---|
PyExc_GreenletError | greenlet.error |
PyExc_GreenletExit | greenlet.GreenletExit |
Reference
PyGreenlet_Import()
一个宏指令,引入greenlet模块,初始化C API。一旦扩展模块使用了greenlet的C API 就必须调用这个。
int PyGreenlet_Check(PyObject *p)
宏指令,如果参数 p是PyGreenlet就返回true
int PyGreenlet_STARTED(PyGreenlet *g)
宏指令,如果greenlet g 已经启动 返回true
int PyGreenlet_ACTIVE(PyGreenlet *g)
宏指令,如果greenlet 对象 g已经启动且没有死亡 返回true
PyGreenlet *PyGreenlet_GET_PARENT(PyGreenlet *g)
宏指令,返回 g的父 greenlet
PyGreenlet *PyGreenlet_SetParent(PyGreenlet *g)
设置 g的父greenlet,如果成功返回0.如果返回-1,g并不是一个指向PyGreenlet的指针 而且会触发 Attribute Error。
PyGreenlet *PyGreenlet_GetCurrent(void)
返回现在活跃状态的greenlet对象
PyGreenlet *PyGreenlet_New(PyObject *run,PyObject *parent)
用于生成一个greenlet对象,参数 run 和 parent都是可选的。如果run 为空,那么greenlet仍然会生成,但是switch的时候就会失败。如果parent为空,则缺省为当前greenlet对象为父。
PyGreenlet *PyGreenlet_Switch(PyGreenlet *g, PyObject *args, PyObject *kwargs)
切换到greenlet对象 g, 后两个参数可为空。如果args为空,那么一个空元素将传递给目标greenlet。如果kwargs为空,那么没有关键参数被传递给目标greenlet。如果参数指定,那么args必定是元祖,kwargs是字典。
PyObject *PyGreenlet_Throw(PyGreenlet *g,PyObject *typ, PyObject *val,PyObject *tb)
切换到greenlet g,但是立即触发异常。typ是异常类型,val是值,tb是可选的,代表回溯对象,可为空。
关于eventlet 和 gevent 这两个以greenlet为核心的并发框架。两个之间的比较 http://blog.gevent.org/2010/02/27/why-gevent/ ,gevent以libevent为核心,最新版本是libev,eventlet主要以纯python写的事件循环为基础,而且在socket方面存在缺陷。不过我认为年代久远,不足以作为凭证,说不定最新的版本的eventlet已经解决了bug。
gevent学习:见英文版http://sdiehl.github.io/gevent-tutorial/ 中文版http://xlambda.com/gevent-tutorial/
eventlet学习 :见 http://eventlet.net/doc/index.html
后续的学习中我将对两个框架进行实践。当然如果使用twisted也是一个很好的解决方案。
来源:
http://greenlet.readthedocs.org/en/latest/
http://gashero.yeax.com/?p=112
http://www.elias.cn/Python/PyConcurrency?from=Develop.PyConcurrency
greenlet微线程的更多相关文章
- 循环引擎 greenlet 没有显式调度的微线程,换言之 协程
小结: 1. micro-thread with no implicit scheduling; coroutines, in other words. 没有显式调度的微线程,换言之 协程 2. 一个 ...
- based on Greenlets (via Eventlet and Gevent) fork 孙子worker 比较 gevent不是异步 协程原理 占位符 placeholder (Future, Promise, Deferred) 循环引擎 greenlet 没有显式调度的微线程,换言之 协程
gevent GitHub - gevent/gevent: Coroutine-based concurrency library for Python https://github.com/gev ...
- paip.提升性能---协程“微线程”的使用.
paip.提升性能---协程的使用. 近乎无限并发的"微线程" 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:h ...
- C# 【一】进程 , 线程 , 微线程 , 同步 , 异步 , 并发 , 并行 , 阻塞 , 非阻塞
一 理解篇 前言 本文仅仅用作借鉴使用,作者刚入行不久,所以请不小心看到这篇文章的朋友,手下留情. 本文以小故事的形式进行叙述,逻辑不通之处.请理解. 如有错误 ,欢迎指出. 谢谢. ...
- Python中greenlet和gevent使用示例
目录 greenlet示例 示例1,线程切换 示例2 gevent 示例1 示例2: gevent使用monkey对所有系统自带的IO操作打patch 示例3,发送请求 示例4:使用gevent的so ...
- Python实例手册
在电脑中突然发现一个这么好的资料,雪松大神制作,不敢独享,特与大家共享.连他的广告也一并复制了吧! python实例手册 #encoding:utf8 # 设定编码-支持中文 0说明 手册制作: 雪松 ...
- 【转】Locust-工具核心原理分析
Locust工具在市场上不如Loadrunner / JMeter流行,使用的范围也没有那么广,但不可否认其是一款很不错的工具.我个人觉得Locust使用不是那么广泛,主要是因为一下方式: Locus ...
- Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)
Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...
- 线程回调,线程中的队列,事件,greenlet模块,gevent模块,自定义补丁, 单线程实现并发,协程
1.线程回调 在线程池/进程池每次提交任务,都会返回一个表示任务的对象,Future对象Future对象具备一个绑定方法,add_done_callback 用于指定回调函数 add 意味着可以添加多 ...
随机推荐
- 查询出各个学科的前3名的同学信息的Sql
查找各个学科的成绩前3名的学生信息Sql,有2种方法,一种是利用sql的row_number() over()函数,另一种是用子查询, 表设计如下 如果不考虑各个学科的成绩有并列的情况的话,有如下两种 ...
- 一些有用的javascript实例分析(三)
原文:一些有用的javascript实例分析(三) 10 输入两个数字,比较大小 window.onload = function () { var aInput = document.getElem ...
- Java得到的一周的最后一天的一段时间内
Java得到的一周的最后一天的一段时间内 1.设计源代码 LastDayOfWeek.java: /** * @Title:LastDayOfWeek.java * @Package:com.you. ...
- ASP.NET MVC学习之控制器篇扩展性
原文:ASP.NET MVC学习之控制器篇扩展性 一.前言 在之前的一篇随笔中已经讲述过控制器,而今天的随笔是作为之前的扩展. 二.正文 1.自定义动作方法 相信大家在开发过程一定会遇到动作方法的重名 ...
- 基于Linux平台病毒BlackHole病毒的决心
今天会见了病毒,少量的代码,但在使用小漏洞的函数的,真正的杀伤力相当惊人. 转载请注明出处:http://blog.csdn.net/u010484477谢谢^_^ watermark/2/text/ ...
- Hadoop上传文件的报错
baidu了很多,都说防火墙,datanode没有正常启动的问题,可是检查了都是正常,后来还是在老外的网站上找到了解决的方法 修改了/etc/security/limits.conf文件,上传成功 这 ...
- Struts2.0+Spring3+Hibernate3(SSH~Demo)
Struts2.0+Spring3+Hibernate3(SSH~Demo) 前言:整理一些集成框架,发现网上都是一些半成品,都是共享一部分出来(确实让人很纠结),这是整理了一份SSH的测试案例,完全 ...
- C# - Recommendations for Abstract Classes vs. Interfaces
The choice of whether to design your functionality as an interface or an abstract class can somet ...
- Entity Framework查询原理
Entity Framework查询原理 前言 Entity Framework的全称是ADO.NET Entity Framework,是微软开发的基于ADO.NET的ORM(Object/Rela ...
- android 使用asm.jar将android手机屏幕投射到电脑
使用asm.jar将Android手机到电脑屏幕投影 有时候可能须要将手机上的一些操作投影出来,比方一些App Demo的展示等.事实上,有专门的硬件设备能干这件事儿.但不是必需专门为展示个Demo去 ...