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

英文最初的小说地址:
汉译转发地址:

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

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

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

1   动机

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

1个”greenlet”,是三个更为原始的微线程的定义,不过并未调度,也许叫做协程。那在你需求控制你的代码时很有用。你能够友善组织微线程的
调度器;也能够利用”greenlet”达成高级的控制流。例如能够另行创立构造器;差异于Python的构造器,大家的构造器能够嵌套的调用函数,而被
嵌套的函数也能够 yield
3个值。(此外,你并不需求三个”yield”关键字,参考例子)。

Greenlet是用作二个C扩充模块给未修改的解释器的。

1.1   例子

假定系统是被控制台程控的,由用户输入指令。假如输入是多个个字符的。那样的连串有如如下的规范:

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,而多数GUI是事件驱动的。他们会在每便的用户输入时调用回调函数。那种场所下,就很难完成read_next_char() 函数。大家有七个不匹配的函数:

def event_keydown(key):
    ??

def read_next_char():
    ?? 必要等待 event_keydown() 的调用

您恐怕在设想用线程完毕。而 格林let
是另一种缓解方案,没有锁和关闭难题。你运转 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() 来用到3个情况机中。

2   使用

2.1   简介

3个 “greenlet”
是3个非常小的独门微线程。能够把它想像成四个堆栈帧,栈底是从头调用,而栈顶是当前greenlet的中止地方。你使用greenlet创制一堆那样的堆
栈,然后在她们中间跳转执行。跳转不是相对的:一个greenlet必须选拔跳转到选拔好的另三个greenlet,这会让前多个挂起,而后贰个恢复生机。多个greenlet之间的跳转称为 切换(switch) 。

当您成立一个greenlet,它拿走三个初阶化过的空堆栈;当你首先次切换来它,他会运行钦赐的函数,然后切换跳出greenlet。当最后栈底
函数结束时,greenlet的仓库又编制程序空的了,而greenlet也就死掉了。greenlet也会因为1个未捕捉的万分死掉。

例如:

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()
包括1个打字与印刷错误(typo),他会转移一个 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,它用于干掉贰个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

    若是1个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的杂质收集

要是不再有对greenlet对象的引用时(包含其余greenlet的parent),照旧不曾艺术切换回greenlet。那种景观下会变卦三个格林letExit
非凡到greenlet。这是greenlet收到异步相当的绝无仅有情状。应该提交3个 try ..
finally
用于清理greenlet内的财富。那些效果并且同意greenlet中十分循环的编制程序风格。那样循环能够在结尾多少个引用消失时自动刹车。

设若不指望greenlet死掉恐怕把引用放到别处,只供给捕捉和忽视 格林letExit
非常即可。

greenlet不参预垃圾收集;greenlet帧的大循环引用数据会被检查和测试到。将引用传递到任何的循环greenlet会挑起内部存款和储蓄器走漏。

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

翻译自官方文书档案greenlet。

背景

greenlet包是Stackless的衍生产品,它是三个帮忙微线程(叫tasklets)的CPython版本。Tasklets运转在伪并发方式下(平时在二个或少于的OS级别的线程),他们经过“channels”来交互数据。

一边来说,
七个“greenlet”任然是1个未曾内部调度的关于微线程的相比原始的定义。换句话说,当您想要在您代码运转时成功规范控制,“greenlet”是一种很有用的点子。在greenlet基础之上,你能够定义本身的微线程调度策略。不管如何,greenlets也能够以一种尖端控制流结构的章程用于他们友善。举个例子,我们能够重复生成迭代器。python自带的生成器与greenlet的生成器之间的界别是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表达的是,你不必要选取yield关键词,参见例子:test_generator.py)。

背景

greenlet包是Stackless的衍生产品,它是2个帮忙微线程(叫tasklets)的CPython版本。Tasklets运转在伪并发情势下(平时在二个或个其余OS级别的线程),他们通过“channels”来交互数据。

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

什么是greenlet

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

greenlet是透过C扩充完成的。

例子

轻量级并发程序,使用Python中的greenlet包达成产出编制程序的入门教程。我们来设想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成套工具是基于事件驱动的。他们为每一个用户字符输入调用2个回调函数。(将“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(),将其更换为状态机。

例子

咱俩来设想三个用户输入指令的顶点控制台系统。假如输入是每个字符输入。在如此的1个系统中,有个独立的大循环如下所示:

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程序中,绝当先55%的GUI成套工具是依照事件驱动的。他们为每3个用户字符输入调用3个回调函数。(将“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(),将其转移为状态机。

示例

有如此2个系统,它依照用户在顶峰输入指令的不等而实施分化的操作,借使输入是逐字符的。部分代码大概是这么的:

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”
是小型的独自的伪线程。考虑到作为1个帧堆栈。最远的帧(最底部)是你调用的初期的函数,最外面的帧(最顶层)是在近日greenlet被压进去的。当你利用greenlets的时候是透过创办一多重的那种堆栈,然后在她们之间跳转执行。那种跳转将会导致原先的帧挂起,最后的帧从挂起状态上升。在greenlets之间的跳转关系叫做“switching(切换)”。

当你创制三个greenlet,它将有多少个开始化的空堆栈。当您首先次切换到它,它伊始运营2个有血有肉的函数。在那么些函数中大概调用别的的函数,从此时此刻greenlet中切换出去,等等。当最底部的函数完结实施,greenlet的栈再一次为空,那时,greenlet归西。greenlet也说不定应2个未捕获的十分而停下。

举个例证:

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”
是小型的单独的伪线程。考虑到作为3个帧堆栈。最远的帧(最底部)是你调用的初期的函数,最外侧的帧(最顶层)是在当前greenlet被压进去的。当你选取greenlets的时候是因而成立一多重的那种堆栈,然后在他们中间跳转执行。那种跳转将会招致原先的帧挂起,最后的帧从挂起状态恢复生机。在greenlets之间的跳转关系叫做“switching(切换)”。

当你创造二个greenlet,它将有二个开头化的空堆栈。当您首先次切换来它,它开端运转一个切实的函数。在那些函数中可能调用其余的函数,从脚下greenlet中切换出去,等等。当最尾部的函数达成实施,greenlet的栈再一次为空,那时,greenlet寿终正寝。greenlet也大概应一个未捕获的10分而平息。

举个例子:

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’。

当您创立三个协程时,发生2个空的栈,在第二次切换到这几个协程时,它调用三个异样的函数,这几个函数中得以调用其余函数,可以切换来别的协程等等。当最后栈底函数执行完后,协程的栈变为空,那时候,协程是死的(dead)。协程也也许是因为那2个而归西。

上面是个卓殊简单的例子:

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都有2个父级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是当2个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中学继续执行。这么说的话,协程是树结构的。最上层的代码不是运作在用户定义的协程中,而是在一个隐式的主协程中,它是协程树的根(root)。

在地点的例证中,gr1和gr2的父协程都是主协程。不管哪三个死去,执行都会再次来到主协程。

12分也会被传到父协程。比如说,test第22中学若包蕴了一个’typo’,就会掀起NameError极度,然后杀死gr2,执行会一向回到主协程。Traceback会展现test2而不是test1。注意,协程的切换不是调用,而是在平行的”栈容器”中传送执行。

实例化对象

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

  • greenlet(run=None,parent=None):创制2个新的greenlet对象(还一直不初叶运转)。run是二个可调用的函数,用来被调用。parent定义父级greenlet,暗中同意是方今greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:这一个新鲜的不得了不会抛出到父级greenlet中,那能够用来杀死1个纯粹的greenlet。

greenlet类型能够被子类化。通过调用在greenlet创设的时候开端化的run属性来推行三个greenlet。可是对于子类来说,定义二个run方法比提供三个run参数给构造器更有意义。

实例化对象

greenlet.greenlet是贰个体协会程类型,它扶助一下操作:

  • greenlet(run=None,parent=None):创立叁个新的greenlet对象(还没有开首运营)。run是二个可调用的函数,用来被调用。parent定义父级greenlet,默许是当下greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:这一个特出的要命不会抛出到父级greenlet中,那足以用来杀死三个纯粹的greenlet。

greenlet类型能够被子类化。通过调用在greenlet创立的时候伊始化的run属性来推行2个greenlet。但是对于子类来说,定义四个run方法比提供2个run参数给构造器更有意义。

协程类

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

切换

当在三个greenlet中调用方法switch(),在greenlet之间的切换将生出,符合规律处境下,程序执行跳转到switch()被调用的greenlet中。或许当1个greenlet离世,程序执行将跳转到父级greenlet程序中,当产生切换的时候,3个目的或三个万分被发送到指标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创制的时候给的,而是在第3回切换的时候给出。

此处给出了有关发送的数额的备受关注标平整:

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程序中,当产生切换的时候,一个对象或多少个11分被发送到指标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(run=None, parent=None)

创办2个新的协程对象。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执行,可是及时抛出1个加以的那多少个。若是没有参数提供,暗许至极是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执行,然则及时抛出二个加以的至极。借使没有参数提供,暗中认可非凡是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线程结合起来。那种意况下,每一个线程包罗三个独立的蕴藏1个子greenlets树的“main”
greenlet。混合或切换在不一致线程中的greenlets是不恐怕的事体。

Greenlets与python的线程

格林lets将得以和python线程结合起来。那种景况下,每三个线程包括三个独门的蕴藏一个子greenlets树的“main”
greenlet。混合或切换在不相同线程中的greenlets是不容许的事体。

greenlet.GreenletExit

其一尤其的相当不会传给父协程,常用来杀死协程。

greenlet是足以被一连的。协程通过执行run属性来运行。在子类中,能够肆意地去定义run,而不是必定要传送run参数给构造器。

greenlets的污物回收生命周期

假设对3个greenlet的有所涉嫌都已经失效(包涵来自其余greenlets中的父级属性的关联),那时候,没有其余一种艺术切换回该greenlet中。那种景色下,GreenletExit非常将会发出。那是一个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不插足垃圾回收。循环那五个在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):设置七个新的调用堆栈方法,重回中期已部分艺术只怕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 相关

Greenlets能够透过用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

二个可调用对象,当g初始实践时,调用它。不过假若起先进行后,这一个特性就不存在了。

关联

  • PyGreenlet_Import():贰个宏定义,导入greenlet模块,开端化C
    API。必须在每1个用到greenlet C API的模块中调用一次。
  • int PyGreenlet_Check(PyObject
    *p):一个宏定义,如果参数是Py格林let重回true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):八个宏定义,假如greenlet在上马了归来true。
  • int PyGreenlet_ACTIVE(PyGreenlet
    *g):1个宏定义,假使greenlet在移动中回到true。
  • PyGreenlet *PyGreenlet_GET_PARENT(PyGreenlet
    *g):1个宏定义,重临greenlet中的父级greenlet。
  • int PyGreenlet_SetParent(PyGreenlet *g, PyGreenlet
    *nparent):设置父级greenlet。重回0为设置成功,-1,表示g不是一使得的PyGreenlet指针,AttributeError将抛出。
  • PyGreenlet
    *PyGreenlet_GetCurrent(void):重临当前活蹦乱跳的greenlet对象。
  • PyGreenlet *PyGreenlet_New(PyObject *run, PyObject
    *parent):使用run与parent成立2个新的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应该是二个tuple,kwargs应该是三个dict。
  • PyObject *PyGreenlet_Throw(PyGreenlet *g, PyObject *typ,
    PyObject *val, PyObject *tb):切换来greenlet
    g,并且及时抛出typ参数(指导的值val)钦点的要命,调用堆栈对象tb是可选的,并且可以为NULL。

关联

  • 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):3个宏定义,假如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创立一个新的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应该是三个tuple,kwargs应该是一个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线程

协程能够和线程组合使用。每一种线程包涵一个独自的主协程和协程树。当然差异线程的协程之间是无能为力切换执行的。

垃圾堆收集

一旦对一个协程的引用计数为0,那么就不可能另行切换到那个体协会程。那种情状下,协程会发生八个格林letExit相当。那是协程唯一一种异步接收到格林letExit非凡的气象。能够用try…finally…来清除协程的财富。那个特点允许大家用最佳循环的点子来等待数据并拍卖,因为当协程的引用计数变成0时,循环会自动刹车。

在Infiniti循环中,若是想要协程身故就抓获GreenletExit相当。倘诺想拥有3个新的引用就大意格林letExit。

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

追踪帮助

当大家利用协程的时候,标准的Python追踪和总体性分析无能为力,因为协程的切换时在单个线程中。很难通过不难的办法来侦测到协程的切换,所以为了增加对调节的帮衬,扩充了上边多少个新的函数:

greenlet.gettrace()

再次回到此前的寻踪函数设置,或许None。

greenlet.settrace(callback)

安装1个新的追踪函数,重回此前的,恐怕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地图