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 意味着可以添加多 ...
随机推荐
- 使用 CodeIgniter 框架快速开发 PHP 应用(六)
原文:使用 CodeIgniter 框架快速开发 PHP 应用(六) 简化使用 Session 和安全理论说得够多了! 现在让我们开始写我们自己的应用. 在这一章里,我们将会大致描述一下我们要建立的一 ...
- LINUX2.4.x网络安全框架
在分析LINUX2.4.x网络安全的实现之前先简介一下它里面包括的几个重要概念:netfilter.iptables.match.target.nf_sockopt_ops.网络安全功能点的实现 ...
- int有符号和无符号类型内存 -- C
/* int 有符号 0xffffffff == -1 0xfffffffe == -2 最小 0x80000000 == -21 4748 3648 最大 0x7fffffff == 21 4748 ...
- jQuery - 基于serializeArray的serializeObject
将表单序列化成JSON对象,注意不管是自实现的serializeObject()还是原生的serializeArray(),所要序列化的控件都必须要有name,而不是id jQuery.prototy ...
- 读取上传的CSV为DataTable
csv导入文件会把每列的数据用英文逗号分割开来,如果遇到某列中包含英文逗号,则会把该列用英文双引号进行包装. 如果csv文件中某列的数据本身包含英文逗号,应该使用读取字符串的方式进行解析数据,如csv ...
- UVA - 10014 - Simple calculations (经典的数学推导题!!)
UVA - 10014 Simple calculations Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & ...
- 随想录(从apple的swift语言说起)
[ 声明:版权全部,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 喜欢apple的程序猿朋友对wwdc肯定不会陌生.本次wwdc上最大的一个亮点之中的一个就是s ...
- jquery 直接调用 wcf,面向服务的SOA架构 ( 第二天)
在前面的基础上,我们来开始第二天编写 客户端 的东西,不过讲之前,我想告诉大家的是: 这个简单的SOA的架构,我们直接通过wcf 调用到 后台的方法, 而中间没有使用 C#代码,大大减少我们客户端的代 ...
- 完整具体解释GCD系列(二)dispatch_after;dispatch_apply;dispatch_once
原创Blog,转载请注明出处 本文阅读的过程中,如有概念不懂,请參照前专栏中之前的文章,假设还有疑惑,请留言. 这是我关于GCD专栏的地址 http://blog.csdn.net/column/de ...
- 访问Ice-Pick Lodge:假设公众筹款网站Kickstarter在成功
Xsolla非常高兴採訪了来自莫斯科的工作室 Ice-Pick Lodge的Golubeva.数天前,该公司已成功在Kickstarter上募集资金,创造出最知名的游戏"Pathologic ...