应用Python中的greenlet包达成产出编制程序的入门教程,轻量级并发程序

By admin in 4858.com on 2019年3月30日

英文原稿地址:
汉译转发地址:

[译文][转载]greenlet:轻量级并发程序,greenlet

英文原来的书文地址:
汉译转发地址:

运用Python中的greenlet包落成产出编制程序的入门教程,pythongreenlet

1   动机

greenlet 包是 Stackless 的副产品,其将微线程称为 “tasklet”
。tasklet运营在伪并发中,使用channel进行同步数据交流。

二个”greenlet”,是八个一发原始的微线程的定义,可是尚未调度,或许叫做协程。那在您必要控制你的代码时很有用。你能够友善组织微线程的
调度器;也得以行使”greenlet”实现高级的控制流。例如可以重复创制构造器;差异于Python的构造器,大家的构造器能够嵌套的调用函数,而被
嵌套的函数也得以 yield
1个值。(别的,你并不须要二个”yield”关键字,参考例子)。

格林let是当做多少个C扩充模块给未修改的解释器的。

1.1   例子

若果系统是被控制台程控的,由用户输入指令。假使输入是3个个字符的。那样的类别有如如下的规范:

def process_commands(*args):
  while True:
    line=''
    while not line.endswith('\n'):
      line+=read_next_char()
    if line=='quit\n':
      print "are you sure?"
      if read_next_char()!="y":
        continue  #忽略指令
    process_commands(line)

现行反革命假如你要把程序移植到GUI,而超越33.33%GUI是事件驱动的。他们会在历次的用户输入时调用回调函数。那种景况下,就很难达成read_next_char() 函数。大家有七个不匹配的函数:

def event_keydown(key):
    ??

def read_next_char():
    ?? 供给拭目以俟 event_keydown() 的调用

你恐怕在考虑用线程实现。而 Greenlet
是另一种缓解方案,没有锁和倒闭难点。你运转 process_commands()
函数,分割成 greenlet ,然后与按键事件交互,有如:

def event_keydown(key):
  g_processor.switch(key)

def read_next_char():
  g_self=greenlet.getcurrent()
  next_char=g_self.parent.switch()  #跳到上一层(main)的greenlet,等待下一次按键
  return next_char

g_processor=greenlet(process_commands)
g_processor.switch(*args)
gui.mainloop()

以此事例的施行流程是: read_next_char() 被调用,也就是 g_processor
的一局地,它就会切换(switch)到她的父greenlet,并假使继续在甲级主循环中履行(GUI主循环)。当GUI调用
event_keydown() 时,它切换来 g_processor
,那意味执行会跳回到原先挂起的地方,约等于 read_next_char()
函数中的切换指令这里。然后 event_keydown() 的 key 参数就会被传送到
read_next_char() 的切换处,并再次来到。

注意 read_next_char()
会被挂起并如若其调用栈会在还原时爱慕的很好,所以她会在被调用的地方回到。那允许程序逻辑保持美貌的顺序流。我们无需重写
process_commands() 来用到2个景况机中。

2   使用

2.1   简介

3个 “greenlet”
是三个十分的小的独自微线程。能够把它想像成1个堆栈帧,栈底是发端调用,而栈顶是时下greenlet的中止地点。你使用greenlet创造一堆那样的堆
栈,然后在他们中间跳转执行。跳转不是纯属的:2个greenlet必须选用跳转到选拔好的另1个greenlet,那会让前四个挂起,而后二个回复。两个greenlet之间的跳转称为 切换(switch) 。

当您创建贰个greenlet,它拿走一个伊始化过的空堆栈;当你首先次切换来它,他会运行内定的函数,然后切换跳出greenlet。当最终栈底
函数甘休时,greenlet的堆栈又编制程序空的了,而greenlet也就死掉了。greenlet也会因为3个未捕捉的十二分死掉。

例如:

from py.magic 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()

末段一行跳转到 test1() ,它打字与印刷12,然后跳转到 test2()
,打字与印刷56,然后跳转回 test1() ,打字与印刷34,然后 test1()
就终止,gr1死掉。那时执行会重临原来的 gr1.switch()
调用。注意,78是不会被打字与印刷的。

2.2   父greenlet

明日看看一个greenlet死掉时实施点去哪个地方。每一种greenlet拥有三个父greenlet。父greenlet在每一种greenlet开首化时被创建(可是能够在别的时候改变)。父greenlet是当greenlet死掉时,继续原来的职分执行。那样,greenlet就被集体成一棵
树,一流的代码并不在用户创制的 greenlet
中运作,而名为主greenlet,也等于树根。

在上边包车型地铁例子中,gr1和gr2都是把主greenlet作为父greenlet的。任何二个死掉,执行点都会回到主函数。

未捕获的足够会波及到父greenlet。借使地点的 test2()
包罗三个打字与印刷错误(typo),他会转变1个 NameError
而干掉gr2,然后实施点会回到主函数。traceback会呈现 test2() 而不是
test1()
。记住,切换不是调用,可是执行点能够在互动的栈容器间并行调换,而父greenlet定义了栈最初从什么地方来。

2.3   实例

py.magic.greenlet 是三个 greenlet 类型,帮衬如下操作:

greenlet(run=None,parent=None)

   
创造三个greenlet对象,而不执行。run是进行回调,而parent是父greenlet,缺省是近来greenlet。

greenlet.getcurrent()

    重回当前greenlet,也正是哪个人在调用这些函数。

greenlet.GreenletExit

    那些一定的可怜不会波及到父greenlet,它用来干掉1个greenlet。

greenlet 类型能够被一连。多少个greenlet通过调用其 run
属性执行,正是创设时钦赐的不胜。对于子类,能够定义一个 run()
方法,而无需严酷服从在构造器中付出 run 参数。

2.4   切换

greenlet之间的切换产生在greenlet的 switch()
方法被调用时,那会让实施点跳转到greenlet的 switch()
被调用处。也许在greenlet死掉时,跳转到父greenlet那里去。在切换时,八个指标或特别被发送到目的greenlet。那能够用作多少个greenlet之间传递消息的便宜格局。例如:

def test1(x,y):
  z=gr2.switch(x+y)
  print z

def test2(u):
  print u
  gr1.switch(42)

gr1=greenlet(test1)
gr2=greenlet(test2)
gr1.switch("hello"," world")

这会打字与印刷出 “hello world” 和42,前边边的例子的出口顺序相同。注意 test1()
和 test2() 的参数并不是在 greenlet
创造时内定的,而是在首先次切换成那里时传递的。

那边是准确的调用情势:

g.switch(obj=None or *args)

切换成实施点greenlet
g,发送给定的目的obj。在奇特景况下,假使g还没有运转,就会让它运行;那种情形下,会传送参数过去,然后调用
g.run(*args) 。

垂死的greenlet

    假如二个greenlet的 run() 甘休了,他会重返值到父greenlet。借使 run()
是可怜终止的,分外会涉及到父greenlet(除非是 greenlet.格林letExit
万分,那种情形下越发会被捕捉并再次来到到父greenlet)。

除去上边的情景外,指标greenlet会收取到发送来的靶子作为 switch()
的重返值。就算 switch()
并不会即时赶回,可是它还是会在今后某一点上回来,当其他greenlet切换回来时。当那产生时,执行点恢复生机到
switch() 之后,而 switch() 重临刚才调用者发送来的目的。那意味着
x=g.switch(y)
会发送对象y到g,然后等着二个不精通是何人发来的对象,并在那边再次来到给x。

瞩目,任何尝试切换成死掉的greenlet的行为都会切换成死掉greenlet的父greenlet,或然父的父,等等。最后的父正是main greenlet,永远不会死掉的。

2.5   greenlet的法门和脾气

g.switch(obj=None or *args)

    切换执行点到greenlet g,同上。

g.run

    调用可实施的g,并运转。在g运行后,那几个性子就不再存在了。

g.parent

    greenlet的父。那是可写的,可是分裂意创立循环的父关系。

g.gr_frame

    当前顶尖帧,或然None。

g.dead

    判断是或不是已经死掉了

bool(g)

    假如g是虎虎有生气的则赶回True,在并未运转只怕终止后赶回False。

g.throw([typ,[val,[tb]]])

    切换执行点到greenlet
g,可是及时抛出钦命的丰硕到g。借使没有提供参数,万分缺省正是greenlet.格林letExit
。依照非凡波及规则,有如上面描述的。注意调用那个点子一致如下:

  def raiser():
    raise typ,val,tb

  g_raiser=greenlet(raiser,parent=g)
  g_raiser.switch()

2.6   Greenlet与Python线程

greenlet能够与Python线程一起利用;在那种境况下,每一种线程包涵贰个独立的
main
greenlet,并富有本身的greenlet树。分歧线程之间不可能相互切换greenlet。

2.7   活动greenlet的杂质收集

应用Python中的greenlet包达成产出编制程序的入门教程,轻量级并发程序。若是不再有对greenlet对象的引用时(包涵别的greenlet的parent),照旧不曾办法切换回greenlet。那种场所下会生成五个格林letExit
分外到greenlet。那是greenlet收到异步很是的唯一情形。应该提交一个 try ..
finally
用于清理greenlet内的能源。这些效能并且允许greenlet中但是循环的编制程序风格。那样循环能够在最终三个引用消失时自动刹车。

一经不期待greenlet死掉大概把引用放到别处,只必要捕捉和大意 格林letExit
卓殊即可。

greenlet不插手垃圾收集;greenlet帧的循环引用数据会被检查和测试到。将引用传递到别的的循环greenlet会引起内存走漏。

1 动机 greenlet 包是 Stackless 的副产品,其将微线程称为 “tasklet”
。tasklet运营…

翻译自官方文书档案greenlet。

背景

greenlet包是Stackless的衍生产品,它是3个支撑微线程(叫tasklets)的CPython版本。Tasklets运维在伪并发方式下(平常在1个或个别的OS级其他线程),他们经过“channels”来交互数据。

一方面来说,
二个“greenlet”任然是三个从未中间调度的关于微线程的相比较原始的概念。换句话说,当您想要在您代码运维时做到准确控制,“greenlet”是一种很有用的方法。在greenlet基础之上,你能够定义自身的微线程调度策略。不管怎样,greenlets也足以以一种尖端控制流结构的点子用于他们协调。举个例子,大家能够重新生成迭代器。python自带的生成器与greenlet的生成器之间的分别是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充说明的是,你不须要动用yield关键词,参见例子:test_generator.py)。

背景

greenlet包是Stackless的衍生产品,它是2个支撑微线程(叫tasklets)的CPython版本。Tasklets运营在伪并发格局下(平常在七个或少数的OS级其他线程),他们经过“channels”来交互数据。

一方面来说,
3个“greenlet”任然是一个从未有过内部调度的有关微线程的较为原始的定义。换句话说,当你想要在你代码运行时形成规范控制,“greenlet”是一种很有用的措施。在greenlet基础之上,你能够定义本人的微线程调度策略。不管怎么着,greenlets也得以以一种高级控制流结构的不二法门用于他们本身。举个例子,大家得以重新生成迭代器。python自带的生成器与greenlet的生成器之间的界别是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表达的是,你不需求运用yield关键词,参见例子:test_generator.py)。

什么是greenlet

greenlet是从Stackless中分离的类型。greenlet也叫微线程、协程,它的调度是由程序明显控制的,所以实行流程是一定的、明确的。而线程的调度完全交由操作系统,执行各类不能预想。同时,协程之间切换的代价远比线程小。

greenlet是因而C扩大完毕的。

例子

小编们来设想一个用户输入指令的极端控制台系统。若是输入是每种字符输入。在这么的二个体系中,有个卓越的轮回如下所示:

def process_commands(*args):
    while True:
        line = ''
        while not line.endswith('\n'):
            line += read_next_char()
        if line == 'quit\n':
            print("are you sure?")
            if read_next_char() != 'y':
                continue    # ignore the command
        process_command(line)

目前,假诺你将顺序移植到GUI程序中,绝超越半数的GUI成套工具是基于事件驱动的。他们为每一个用户字符输入调用二个回调函数。(将“GUI”替换来“XML
expat
parser”,对您来说应该越来越熟习了)。在如此的状态中,执行下面包车型客车函数read_next_char()是很拮据的。那里是多个不兼容的函数:

def event_keydown(key):
    ??

def read_next_char():
    ?? should wait for the next event_keydown() call

你大概考虑用线程的艺术来落到实处这些了。greenlets是另一种不供给关联锁与没有当机难题的可选的解决方案。你执行process_commands(),独立的greenlet。通过如下格局输入字符串。

def event_keydown(key):
         # jump into g_processor, sending it the key
    g_processor.switch(key)

def read_next_char():
        # g_self is g_processor in this simple example
    g_self = greenlet.getcurrent()
        # jump to the parent (main) greenlet, waiting for the next key
    next_char = g_self.parent.switch()
    return next_char

g_processor = greenlet(process_commands)
g_processor.switch(*args)   # input arguments to process_commands()

gui.mainloop()

以此事例中,执行流程如下:

  • 当作为g_processor
    greenlet一有的的read_next_char()函数被调用,所以当接受到输入切换来上级greenlet,
    程序苏醒到主循环(GUI)执行。
  • 当GUI调用event_keydown()的时候,程序切换来g_processor。那就代表程序跳出,无论它被挂起在那些greenlet什么地方。在那个事例中,切换来read_next_char(),并且在event_keydown()中被按下的key作为switch()的结果回到给了read_next_char()。

内需证实的是read_next_char()的挂起与还原都封存其调用堆栈。以便在prprocess_commands()中依据她来的地点复苏到不一样的职位。那使得以一种好的主宰流来控制造进度序逻辑成为可能。大家不要完全的重写process_commands(),将其转移为状态机。

例子

大家来设想3个用户输入指令的顶峰控制台系统。若是输入是每一个字符输入。在那样的3个种类中,有个典型的巡回如下所示:

def process_commands(*args):
    while True:
        line = ''
        while not line.endswith('\n'):
            line += read_next_char()
        if line == 'quit\n':
            print("are you sure?")
            if read_next_char() != 'y':
                continue    # ignore the command
        process_command(line)

于今,假诺你将次第移植到GUI程序中,绝半数以上的GUI成套工具是依据事件驱动的。他们为每贰个用户字符输入调用多个回调函数。(将“GUI”替换来“XML
expat
parser”,对您来说应该更为熟知了)。在这么的景观中,执行上边包车型大巴函数read_next_char()是很艰辛的。那里是四个不匹配的函数:

def event_keydown(key):
    ??

def read_next_char():
    ?? should wait for the next event_keydown() call

你恐怕考虑用线程的主意来达成这么些了。greenlets是另一种不必要关联锁与没有当机难点的可选的化解方案。你执行process_commands(),独立的greenlet。通过如下形式输入字符串。

def event_keydown(key):
         # jump into g_processor, sending it the key
    g_processor.switch(key)

def read_next_char():
        # g_self is g_processor in this simple example
    g_self = greenlet.getcurrent()
        # jump to the parent (main) greenlet, waiting for the next key
    next_char = g_self.parent.switch()
    return next_char

g_processor = greenlet(process_commands)
g_processor.switch(*args)   # input arguments to process_commands()

gui.mainloop()

那几个事例中,执行流程如下:

  • 当作为g_processor
    greenlet一部分的read_next_char()函数被调用,所以当接到到输入切换成上面greenlet,
    程序恢复生机到主循环(GUI)执行。
  • 当GUI调用event_keydown()的时候,程序切换成g_processor。那就表示程序跳出,无论它被挂起在那个greenlet什么位置。在这么些事例中,切换成read_next_char(),并且在event_keydown()中被按下的key作为switch()的结果再次来到给了read_next_char()。

亟需注明的是read_next_char()的挂起与还原都保存其调用堆栈。以便在prprocess_commands()中依据她来的地点复苏到分裂的任务。那使得以一种好的支配流来控制造进度序逻辑成为大概。大家不用完全的重写process_commands(),将其更换为状态机。

示例

有如此贰个体系,它依照用户在顶峰输入指令的不等而施行分裂的操作,若是输入是逐字符的。部分代码大概是这么的:

def process_commands(*args):
    while True:
        line = ''
        while not line.endswith('\n'):
            line += read_next_char()
        if line == 'quit\n':
            print "are you sure?"
            if read_next_char() != 'y':
                continue    # 忽略当前的quit命令
        process_command(line)

如今大家想把这几个程序在GUI中落到实处。可是抢先八分之四GUI库都以事件驱动的,每当用户输入都会调用三个回调函数去处理。在那种情状下,假如还想用上面的代码逻辑,大概是这么的:

def event_keydown(key):
    ??

def read_next_char():
    ?? # 必须等待下一个event_keydown调用

read_next_char要阻塞等待event_keydown调用,然后就会和事件循环相争论。那种要求出现的动静是能够用八线程来拍卖,可是我们有更好的不二法门,正是greenlet。

def event_keydown(key):
         # 跳到g_processor,将key发送过去
    g_processor.switch(key)

def read_next_char():
        # 在这个例子中,g_self就是g_processor
    g_self = greenlet.getcurrent()
        # 跳到父greenlet,等待下一个Key
    next_char = g_self.parent.switch()
    return next_char

g_processor = greenlet(process_commands)
g_processor.switch(*args)

gui.mainloop()

咱俩先用process_commands开创三个体协会程,然后调用switch切换成process_commands中去实践,并输入参数args。在process_commands中运作到read_next_char,又切换来主协程,执行gui.mainloop(),在事件循环中等待键盘按下的动作。当按下有个别键之后,调用event_keydown,切换到g_processor,并将key传过去。read_next_char还原运转,接收到key,然后回到给process_commands,处理完之后又暂停在read_next_char等候下一遍按键。

下边大家来详细讲解greenlet的用法。

用法

用法

用法

序言

“greenlet”
是小型的独门的伪线程。考虑到作为四个帧堆栈。最远的帧(最底部)是您调用的初期的函数,最外面包车型客车帧(最顶层)是在日前greenlet被压进去的。当您使用greenlets的时候是透过创办一各种的那种堆栈,然后在她们之间跳转执行。那种跳转将会导致原先的帧挂起,最终的帧从挂起状态回涨。在greenlets之间的跳转关系叫做“switching(切换)”。

当你创制贰个greenlet,它将有1个开端化的空堆栈。当您首先次切换来它,它起初运转叁个有血有肉的函数。在这么些函数中恐怕调用别的的函数,从此时此刻greenlet中切换出去,等等。当最底部的函数实现实施,greenlet的栈再次为空,那时,greenlet去世。greenlet也说不定应一个未捕获的格外而偃旗息鼓。

举个例证:

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()
  • 最后一行跳转到test1, 然后打字与印刷12,
  • 跳转到test2, 然后打字与印刷56
  • 跳转回test1, 打字与印刷34,
    test1完结,并且gr1去世。与此同时,程序执行再次来到到gr1.switch()调用。
  • 亟待注解的是78一直都并未打印。

序言

“greenlet”
是微型的独自的伪线程。考虑到作为一个帧堆栈。最远的帧(最尾部)是你调用的早先时代的函数,最外侧的帧(最顶层)是在当前greenlet被压进去的。当你选用greenlets的时候是由此创制一多元的那种堆栈,然后在他们中间跳转执行。那种跳转将会招致原先的帧挂起,最终的帧从挂起状态苏醒。在greenlets之间的跳转关系叫做“switching(切换)”。

当您创造贰个greenlet,它将有2个开端化的空堆栈。当你首先次切换成它,它早先运营1个具体的函数。在这些函数中或者调用别的的函数,从此时此刻greenlet中切换出去,等等。当最尾部的函数落成实施,greenlet的栈再一次为空,那时,greenlet病逝。greenlet也或许应三个未捕获的万分而偃旗息鼓。

举个例证:

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()
  • 最终一行跳转到test1, 然后打字与印刷12,
  • 跳转到test2, 然后打字与印刷56
  • 跳转回test1, 打字与印刷34,
    test1完结,并且gr1驾鹤归西。与此同时,程序执行重回到gr1.switch()调用。
  • 内需证实的是78向来都没有打字与印刷。

简介

几个体协会程是二个独自的“假线程”。能够把它想成1个小的帧栈,栈底是你调用的开始函数,栈顶是greenlet当前半上落下的地方。大家选取协程,实际上就是创办了一种种那样帧栈,然后在它们中间跳转执行。而跳转必须是总而言之的,跳转也叫做’switching’。

当您创立八个体协会程时,爆发3个空的栈,在第①次切换来那些体协会程时,它调用3个非同小可的函数,这些函数中能够调用别的函数,能够切换来别的协程等等。当最终栈底函数执行完后,协程的栈变为空,那时候,协程是死的(dead)。协程也可能是因为那些而病逝。

上边是个分外简单的例证:

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()

终极一行切换成test1,打字与印刷12,切换成test2,打字与印刷56,又切回到test1打印34。然后test1甘休,gr1过逝。那时候执行回来了gr1.switch()调用。注意到,78并不曾被打字与印刷出。

父级greenlet

让我们看看当greenlet驾鹤归西的时候,程序执行到哪儿去了。各类greenlet都有1个父级greenlet。最初的父级是开创greenlet的这个greenlet(父级greenlet是能够在其他时候被转移)。父级greenlet是当二个greenlet过逝的时候程序继续执行的地方。这种格局,程序协会成一颗树。不在用户创制的greenlet中运作的顶层代码在隐式的主greenlet中运转,它是堆栈数的根。

在上边的事例中,gr1与gr2将主greenlet作为父级greenlet。无论它们中的哪个人执行完结,程序执行都会回去到”main”greenlet中。

并未捕获的充足将抛出到父级greenlet中。举个例子,如若上面包车型地铁test2()包涵三个语法错误,它将生成三个杀掉gr2的NameError错误,这一个破绽百出将直接跳转到主greenlet。错误堆栈将呈现test2,而不会是test1。需求留意的是,switches不是调用,而是程序在交互的”stack
container(堆栈容器)”直接实施的跳转,“parent”定义了逻辑上位居当前greenlet之下的库房。

父级greenlet

让大家看看当greenlet寿终正寝的时候,程序执行到哪个地方去了。每一个greenlet都有五个父级greenlet。最初的父级是创造greenlet的那多少个greenlet(父级greenlet是足以在其余时候被改动)。父级greenlet是当1个greenlet谢世的时候程序继续执行的地点。那种措施,程序协会成一颗树。不在用户创造的greenlet中运作的顶层代码在隐式的主greenlet中运作,它是堆栈数的根。

在上头的例子中,gr1与gr2将主greenlet作为父级greenlet。无论它们中的什么人执行完结,程序执行都会回到到”main”greenlet中。

并未捕获的可怜将抛出到父级greenlet中。举个例子,如若地点的test2()包涵贰个语法错误,它将生成七个杀掉gr2的NameError错误,那么些指鹿为马将平素跳转到主greenlet。错误堆栈将展现test2,而不会是test1。要求小心的是,switches不是调用,而是程序在竞相的”stack
container(堆栈容器)”直接执行的跳转,“parent”定义了逻辑上位居当前greenlet之下的堆栈。

父协程

每一种体协会程都有一个父协程。协程在哪个体协会程中被创建,那么那一个体协会程便是父协程,当然后边能够更改。当某些协程病逝后,会在父协程中继续执行。举个例子,在g第11中学成立了g2,那么g1正是g2的父协程,g2离世后,会在g第11中学继续执行。这么说的话,协程是树结构的。最上层的代码不是运转在用户定义的协程中,而是在1个隐式的主协程中,它是协程树的根(root)。

在上边的事例中,gr1和gr2的父协程都以主协程。不管哪三个身故,执行都会回到主协程。

老大也会被传到父协程。比如说,test第22中学若包括了一个’typo’,就会抓住NameError非常,然后杀死gr2,执行会直接重临主协程。Traceback会显示test2而不是test1。注意,协程的切换不是调用,而是在平行的”栈容器”中传递执行。

实例化对象

greenlet.greenlet是三个协程类型,它支持一下操作:

  • greenlet(run=None,parent=None):创制1个新的greenlet对象(还尚无伊始运营)。run是贰个可调用的函数,用来被调用。parent定义父级greenlet,暗中同意是现阶段greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:那么些奇特的可怜不会抛出到父级greenlet中,那足以用来杀死三个十足的greenlet。

greenlet类型能够被子类化。通过调用在greenlet制造的时候初阶化的run属性来实施一个greenlet。可是对于子类来说,定义二个run方法比提供三个run参数给构造器更有意义。

实例化对象

greenlet.greenlet是一个体协会程类型,它支持一下操作:

  • greenlet(run=None,parent=None):创立1个新的greenlet对象(还尚无起首运转)。run是2个可调用的函数,用来被调用。parent定义父级greenlet,暗中同意是当前greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:这么些特殊的那多少个不会抛出到父级greenlet中,那能够用来杀死三个十足的greenlet。

greenlet类型能够被子类化。通过调用在greenlet创设的时候初步化的run属性来实施三个greenlet。但是对于子类来说,定义一个run方法比提供2个run参数给构造器更有意义。

协程类

greenlet.greenlet就是协程类,它帮助上边一些操作:

4858.com ,切换

当在多少个greenlet中调用方法switch(),在greenlet之间的切换将产生,平时意况下,程序执行跳转到switch()被调用的greenlet中。也许当3个greenlet谢世,程序执行将跳转到父级greenlet程序中,当爆发切换的时候,1个对象或2个丰硕被发送到目的greenlet中。那是一种在三个greenlet中传递新闻的便宜的办法。举个例子:

def test1(x, y):
    z = gr2.switch(x+y)
    print z

def test2(u):
    print u
    gr1.switch(42)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch("hello", " world")

已与前边例子一样顺序执行,它将会打字与印刷“hello
world”与42。多说一句,test1(),test2()的参数不是在greenlet成立的时候给的,而是在率先次切换的时候给出。

此地给出了有关发送的数指标肯定的条条框框:

g.switch(*args, **kwargs):切换执行到greenlet
g,发送数据,作为二个异样的例证,若是g没有执行,它将早先实践。

对于将死的greenlet。当run()实现的时候,将会发出对象给父级greenlet。倘使greenlet因为极度而停下,这些可怜将会抛出到父级greenlet中(greenlet.格林letExit例外,这几个可怜被捕获了并且直接退出到父级greenlet中)。

除去上边例子描述的,平时目的greenlet(父级greenlet)接收从前调用switch()挂起,执行完成再次回到的再次来到值作为结果。事实上,固然对switch()的调用不会立刻赶回结果,不过当别的部分greenlet切换回来的时候,在以往的某部点将会回到结果。当切换爆发的时候,程序将在它前边挂起的地方复苏。switch()本人回到发生的对象。那就代表x=g.switch(y)将y给g,稍后将回到从有些不关乎的greenlet中回到的不涉及的靶子给x变量。

提示一下,任何准备切换成三个凋谢的greenlet的将会走到谢世greenlet的父级,或然父级的父级,以此类推(最终的父级是“main”
greenlet,它是没有会死掉的)。

切换

当在二个greenlet中调用方法switch(),在greenlet之间的切换将生出,符合规律情状下,程序执行跳转到switch()被调用的greenlet中。恐怕当二个greenlet谢世,程序执行将跳转到父级greenlet程序中,当爆发切换的时候,1个对象或七个要命被发送到指标greenlet中。那是一种在几个greenlet中传递消息的便宜的格局。举个例子:

def test1(x, y):
    z = gr2.switch(x+y)
    print z

def test2(u):
    print u
    gr1.switch(42)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch("hello", " world")

已与事先例子一样顺序执行,它将会打印“hello
world”与42。多说一句,test1(),test2()的参数不是在greenlet创立的时候给的,而是在第四回切换的时候给出。

那边给出了有关发送的数码的明朗的规则:

g.switch(*args, **kwargs):切换执行到greenlet
g,发送数据,作为五个奇特的事例,借使g没有执行,它将起来实行。

对于将死的greenlet。当run()达成的时候,将会产生对象给父级greenlet。若是greenlet因为12分而告一段落,这几个足够将会抛出到父级greenlet中(greenlet.格林letExit例外,这一个那么些被抓走了并且直接退出到父级greenlet中)。

除开下面例子描述的,经常目的greenlet(父级greenlet)接收在此之前调用switch()挂起,执行完成重回的重临值作为结果。事实上,固然对switch()的调用不会立即回去结果,不过当其余部分greenlet切换回来的时候,在今后的某部点将会回到结果。当切换发生的时候,程序将在它前边挂起的地点苏醒。switch()本身回去爆发的目的。那就代表x=g.switch(y)将y给g,稍后将回到从某些不涉及的greenlet中回到的不涉及的目的给x变量。

升迁一下,任何准备切换来叁个死去的greenlet的将会走到归西greenlet的父级,大概父级的父级,以此类推(最终的父级是“main”
greenlet,它是未曾会死掉的)。

greenlet(run=None, parent=None)

创办三个新的协程对象。run是二个可调用对象,parent是父协程,暗中认可是当下协程。

greenlets的办法与品质

  • g.switch(*args, **kwargs):切换程序到greenlet g中履行,参见上边。
  • g.run:当它发轫的时候,g的回调将会被实施,当g已经初阶实践了,那么些性子将不会设有了。
  • g.parent:父级greenlet。那是可编写制定属性,不过无法写成了死循环。
  • g.gr_frame:最顶层的构造,或然等于None。
  • g.dead: bool值,当g死亡了,值为True。
  • bool(g):bool值,当再次回到结构是True,表示g还活蹦乱跳,借使是False,表示它过逝了或许还没起来。
  • g.throw([typ, [val,
    [tb]]]):切换来g执行,不过及时抛出3个加以的那多少个。假使没有参数提供,默许万分是greenlet.格林letExit。同地点描述一样,不荒谬的不行传递规则生效。调用该措施同上面代码是差不多也正是的:

      def raiser():
          raise typ, val, tb
      g_raiser = greenlet(raiser, parent=g)
      g_raiser.switch()
    

    有有些不一的是,那段代码不能够用来greenlet.GreenletExit相当,这些可怜将不会从g_raiser传播到g。

greenlets的主意与天性

  • g.switch(*args, **kwargs):切换程序到greenlet g中推行,参见上面。
  • g.run:当它开始的时候,g的回调将会被实践,当g已经初叶实践了,那天性子将不会设有了。
  • g.parent:父级greenlet。这是可编写制定属性,不过不可见写成了死循环。
  • g.gr_frame:最顶层的布局,或许等于None。
  • g.dead: bool值,当g死亡了,值为True。
  • bool(g):bool值,当重临结构是True,表示g还活蹦乱跳,借使是False,表示它与世长辞了可能还没起来。
  • g.throw([typ, [val,
    [tb]]]):切换成g执行,不过及时抛出1个加以的越发。假使没有参数提供,私下认可十分是greenlet.格林letExit。同地点描述一样,符合规律的十二分传递规则生效。调用该办法同下边代码是大约约等于的:

      def raiser():
          raise typ, val, tb
      g_raiser = greenlet(raiser, parent=g)
      g_raiser.switch()
    

    有有些不一的是,那段代码无法用来greenlet.格林letExit很是,这么些可怜将不会从g_raiser传播到g。

greenlet.getcurrent()

归来当前协程,也等于调用那些函数的协程。

Greenlets与python的线程

格林lets将得以和python线程结合起来。那种景况下,每多少个线程包括三个独立的隐含八个子greenlets树的“main”
greenlet。混合或切换在差别线程中的greenlets是不容许的政工。

Greenlets与python的线程

格林lets将能够和python线程结合起来。那种情况下,每三个线程包涵贰个独门的盈盈一个子greenlets树的“main”
greenlet。混合或切换在不一致线程中的greenlets是不或者的政工。

greenlet.GreenletExit

以此特殊的特别不会传给父协程,常用来杀死协程。

greenlet是能够被接续的。协程通过执行run属性来运作。在子类中,能够轻易地去定义run,而不是早晚要传递run参数给构造器。

greenlets的垃圾堆回收生命周期

只要对二个greenlet的有着涉及都曾经失效(包罗来自其余greenlets中的父级属性的关系),那时候,没有别的一种方式切换回该greenlet中。那种情形下,格林letExit卓殊将会发出。那是2个greenlet接受异步执行的绝无仅有办法。使用try:finally:语句块来清理被那一个greenlet使用的能源。那种性质帮衬一种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的最后关联失效,那种循环将活动截止。

一旦greenlet要么回老家,要么依照留存有个别地点的涉嫌复苏。只须求捕获与忽视或许导致极端循环的格林letExit。

格林lets不参加垃圾回收。循环那多少个在greenlet框架中的数据时候,那几个数量将不会被检查和测试到。循环的囤积其余greenlets的引用将也许引致内部存款和储蓄器泄漏。

greenlets的垃圾回收生命周期

一旦对叁个greenlet的享有关乎都已经失效(包罗来自其余greenlets中的父级属性的关联),那时候,没有别的一种艺术切换回该greenlet中。那种景观下,GreenletExit卓殊将会生出。那是二个greenlet接受异步执行的唯一方式。使用try:finally:语句块来清理被这么些greenlet使用的能源。那种性格援救一种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的最终关联失效,那种循环将自行终止。

假若greenlet要么回老家,要么依据留存有个别地方的关系恢复生机。只供给捕获与忽略恐怕引致极端循环的格林letExit。

格林lets不插足垃圾回收。循环那3个在greenlet框架中的数据时候,这一个多少校不会被检查和测试到。循环的蕴藏别的greenlets的引用将可能造成内部存款和储蓄器泄漏。

切换

有三种情状会生出协程之间的切换。一是有个别体协会程主动调用switch方法,那种情况下会切换来被调用的协程中。二是协程病逝,这时协程会切换来父协程。在切换时,三个指标或尤其被传送到对象协程。那用来在协程之间传递消息。如上面那些例子:

def test1(x, y):
    z = gr2.switch(x+y)
    print z

def test2(u):
    print u
    gr1.switch(42)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch("hello", " world")

其一代码会打字与印刷”hello
world”和42。注意到,test1和test2在协程成立时并没有提供参数,而是在率先次切换的地点。

谬误堆栈支持

当使用greenlet的时候,标准的python错误堆栈与讲述将不会规行矩步预期的运转,因为堆栈与框架的切换产生在一如既往的线程中。使用古板的艺术可相信的检查和测试greenlet切换是一件很困难的作业。因而,为了句斟字酌对greenlet基础代码的调节和测试,错误堆栈,难题讲述的协理,在greenlet模块中,有一对新的法子:

  • greenlet.gettrace():重临先前已有些调用堆栈方法,大概None。
  • greenlet.settrace(callback):设置二个新的调用堆栈方法,再次来到先前时代已部分艺术依然None。当某个事件发生时,这几个回调函数被调用,能够永安里做一下信号处理。

      def callback(event, args):
          if event == 'switch':
              origin, target = args
              # Handle a switch from origin to target.
              # Note that callback is running in the context of target
              # greenlet and any exceptions will be passed as if
              # target.throw() was used instead of a switch.
              return
          if event == 'throw':
              origin, target = args
              # Handle a throw from origin to target.
              # Note that callback is running in the context of target
              # greenlet and any exceptions will replace the original, as
              # if target.throw() was used with the replacing exception.
              return
    

    为了合营,当事件依然是switch要么是throw,而不是别的大概的轩然大波时候,将参数解包成tuple。那样,API或然扩展出于sys.settrace()相似的新的风云。

错误堆栈帮忙

当使用greenlet的时候,标准的python错误堆栈与叙述将不会根据预期的运转,因为堆栈与框架的切换发生在同样的线程中。使用守旧的艺术可信的检查和测试greenlet切换是一件很不方便的事体。因而,为了改革对greenlet基础代码的调节,错误堆栈,难点讲述的支撑,在greenlet模块中,有局地新的法子:

  • greenlet.gettrace():重临先前已有个别调用堆栈方法,或然None。
  • greenlet.settrace(callback):设置1个新的调用堆栈方法,重回早先时代已部分艺术依旧None。当一些事件发生时,那些回调函数被调用,能够永安里做一下信号处理。

      def callback(event, args):
          if event == 'switch':
              origin, target = args
              # Handle a switch from origin to target.
              # Note that callback is running in the context of target
              # greenlet and any exceptions will be passed as if
              # target.throw() was used instead of a switch.
              return
          if event == 'throw':
              origin, target = args
              # Handle a throw from origin to target.
              # Note that callback is running in the context of target
              # greenlet and any exceptions will replace the original, as
              # if target.throw() was used with the replacing exception.
              return
    

    为了合作,当事件照旧是switch要么是throw,而不是别的恐怕的轩然大波时候,将参数解包成tuple。那样,API恐怕扩大出于sys.settrace()相似的新的事件。

g.switch(*args, **kwargs)

切换成协程g执行,传递提供的参数。假诺g还没运营,那么传递参数给g的run属性,并初叶执行run()。

假定协程的run()执行达成,return的值会重返给主协程。假使run()以老大格局收场,格外会传送给主协程(除非是greenlet.GreenletExit,那种情况下会一贯重回到主协程)。

若果切换成3个已与世长辞的的协程,那么实际上是切换来它的父协程,依次类推。

C API 相关

格林lets能够经过用C/C++写的恢宏模块来变化与保证,大概来自于嵌入到python中的应用。greenlet.h
头文件被提供,用来显示对原生的python模块的完全的API访问。

C API 相关

格林lets能够通过用C/C++写的恢宏模块来扭转与保卫安全,可能来自于嵌入到python中的应用。greenlet.h
头文件被提供,用来浮现对原生的python模块的完全的API访问。

协程的不二法门和质量

类型

Type namePython namePyGreenletgreenlet.greenlet

类型

Type namePython namePyGreenletgreenlet.greenlet

g.switch(*args, **kwargs)

切换来协程g执行,见上面。

异常

Type namePython
namePyExc_GreenletErrorgreenlet.errorPyExc_GreenletExitgreenlet.GreenletExit

异常

Type namePython
namePyExc_GreenletErrorgreenlet.errorPyExc_GreenletExitgreenlet.GreenletExit

g.run

3个可调用对象,当g开端履行时,调用它。可是只要初阶执行后,那本特性就不设有了。

关联

  • PyGreenlet_Import():三个宏定义,导入greenlet模块,早先化C
    API。必须在每3个用到greenlet C API的模块中调用一遍。
  • int PyGreenlet_Check(PyObject
    *p):2个宏定义,假若参数是Py格林let重返true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):3个宏定义,就算greenlet在始发了回到true。
  • int PyGreenlet_ACTIVE(PyGreenlet
    *g):三个宏定义,如若greenlet在移动中回到true。
  • PyGreenlet *PyGreenlet_GET_PARENT(PyGreenlet
    *g):一个宏定义,重临greenlet中的父级greenlet。
  • int PyGreenlet_SetParent(PyGreenlet *g, PyGreenlet
    *nparent):设置父级greenlet。重临0为设置成功,-1,表示g不是一实用的Py格林let指针,AttributeError将抛出。
  • PyGreenlet
    *PyGreenlet_GetCurrent(void):重临当前活跃的greenlet对象。
  • PyGreenlet *PyGreenlet_New(PyObject *run, PyObject
    *parent):使用run与parent创设1个新的greenlet对象。那多少个参数是可选的。要是run是NULL。那么些greenlet创制,假使切换初步将破产。假诺parent是NULL。那么些parent将电动设置成当前greenlet。
  • PyObject *PyGreenlet_Switch(PyGreenlet *g, PyObject *args,
    PyObject *kwargs):切换来greenet
    g。args与kwargs是可选的,能够为NULL。借使args为NULL,3个空的tuple将发送给指标greenlet
    g。纵然kwargs是NULL的。没有key-value参数发送。如果钦赐参数,那么args应该是八个tuple,kwargs应该是1个dict。
  • PyObject *PyGreenlet_Throw(PyGreenlet *g, PyObject *typ,
    PyObject *val, PyObject *tb):切换来greenlet
    g,并且立刻抛出typ参数(辅导的值val)钦定的不得了,调用堆栈对象tb是可选的,并且能够为NULL。

关联

  • PyGreenlet_Import():三个宏定义,导入greenlet模块,早先化C
    API。必须在每贰个用到greenlet C API的模块中调用一遍。
  • int PyGreenlet_Check(PyObject
    *p):二个宏定义,倘使参数是PyGreenlet重返true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):贰个宏定义,借使greenlet在开端了回来true。
  • int PyGreenlet_ACTIVE(PyGreenlet
    *g):一个宏定义,要是greenlet在运动中回到true。
  • PyGreenlet *PyGreenlet_GET_PARENT(PyGreenlet
    *g):二个宏定义,再次回到greenlet中的父级greenlet。
  • int PyGreenlet_SetParent(PyGreenlet *g, PyGreenlet
    *nparent):设置父级greenlet。重回0为设置成功,-1,表示g不是一有效的Py格林let指针,AttributeError将抛出。
  • PyGreenlet
    *PyGreenlet_GetCurrent(void):重回当前活蹦乱跳的greenlet对象。
  • PyGreenlet *PyGreenlet_New(PyObject *run, PyObject
    *parent):使用run与parent创制3个新的greenlet对象。那多少个参数是可选的。借使run是NULL。那一个greenlet创建,假如切换开首将失利。假如parent是NULL。那些parent将活动设置成当前greenlet。
  • PyObject *PyGreenlet_Switch(PyGreenlet *g, PyObject *args,
    PyObject *kwargs):切换来greenet
    g。args与kwargs是可选的,能够为NULL。假诺args为NULL,二个空的tuple将发送给目的greenlet
    g。如果kwargs是NULL的。没有key-value参数发送。假设内定参数,那么args应该是1个tuple,kwargs应该是1个dict。
  • PyObject *PyGreenlet_Throw(PyGreenlet *g, PyObject *typ,
    PyObject *val, PyObject *tb):切换成greenlet
    g,并且及时抛出typ参数(辅导的值val)内定的极度,调用堆栈对象tb是可选的,并且能够为NULL。
g.parent

父协程,这一个值是能够改变的,然而差别意创立循环的父过程。

目录与表

目录与表

英文原稿地址:
汉语翻译转发地址:…

g.gr_frame

现阶段最顶层的帧,恐怕是None。

g.dead

比方协程已身故,那么值是True。

bool(g)

一经协程处于活跃状态,则为True。假设已气绝身亡照旧未开首施行则为False。

g.throw([typ, [val, [tb]]])

切换来g执行,不过及时引发那个。假设没有参数,则私下认可引发greenlet.格林letExit十分。那个办法的履行类似于:

def raiser():
    raise typ, val, tb
g_raiser = greenlet(raiser, parent=g)
g_raiser.switch()

当然greenlet.GreenletExit除外。

协程和Python线程

协程能够和线程组合使用。各类线程包括多少个独立的主协程和协程树。当然区别线程的协程之间是心有余而力不足切换执行的。

污源收集

假设对1个体协会程的引用计数为0,那么就不可能重新切换成这一个协程。那种景况下,协程会发出一个格林letExit非凡。这是协程唯一一种异步接收到GreenletExit卓殊的事态。能够用try…finally…来驱除协程的财富。这几个性格允许大家用最为循环的艺术来等待数据并处理,因为当协程的引用计数变成0时,循环会自动刹车。

在最棒循环中,假如想要协程与世长辞就破获格林letExit万分。假使想拥有二个新的引用就马虎格林letExit。

greenlet不参与垃圾收集,如今协程帧的轮回引用数据不会被检查和测试到。循环地将引用存到别的协程会促成内部存款和储蓄器泄漏。

追踪协理

当大家运用协程的时候,标准的Python追踪和本性分析无能为力,因为协程的切换时在单个线程中。很难通过简单的不二法门来侦测到协程的切换,所以为了增强对调剂的辅助,扩展了上面多少个新的函数:

greenlet.gettrace()

回去以前的追踪函数设置,或许None。

greenlet.settrace(callback)

设置二个新的寻踪函数,重回以前的,也许None。这一个函数类似于sys.settrace()各个风云发生的时候都会调用callback,并且callback是上面那样的:

def callback(event, args):
    if event == 'switch':
        origin, target = args
        # 处理从origin到target的切换
        # 注意callback在target的上下文中执行
        return
    if event == 'throw':
        origin, target = args
        # 处理从origin到target的抛出
        # 注意callback在target的上下文中执行
        return

那么下次编写并发程序的时候,是或不是该考虑一下协程呢?

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有