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

英文原稿地址:
中文翻译转发地址:

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

英文原稿地址:
中文翻译转发地址:

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

1   动机

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

三个”greenlet”,是三个更为原始的微线程的概念,但是并未有调度,也许叫做协程。那在您需求控制你的代码时很有用。你能够团结协会微线程的
调度器;也足以接纳”greenlet”达成高级的控制流。例如能够再一次创制构造器;分裂于Python的构造器,大家的构造器能够嵌套的调用函数,而被
嵌套的函数也足以 yield
1个值。(别的,你并不须求三个”yield”关键字,参考例子)。

Greenlet是用作2个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
是另1种缓解方案,未有锁和关闭难点。你运转 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.1   简介

二个 “greenlet”
是二个十分小的独自微线程。可以把它想像成三个堆栈帧,栈底是早先调用,而栈顶是当下greenlet的刹车地方。你选用greenlet成立一批那样的堆
栈,然后在她们之间跳转执行。跳转不是纯属的:多少个greenlet必须挑选跳转到选择好的另1个greenlet,那会让前二个挂起,而后1个上涨。两个greenlet之间的跳转称为 切换(switch) 。

当您创建一个greenlet,它拿走2个初阶化过的空堆栈;当您首先次切换来它,他会运维钦赐的函数,然后切换跳出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()

末尾1行跳转到 test一() ,它打字与印刷12,然后跳转到 test二()
,打印56,然后跳转回 test1() ,打字与印刷34,然后 test一()
就截止,gr一死掉。那时执行会回到原来的 gr壹.switch()
调用。注意,7捌是不会被打字与印刷的。

2.2   父greenlet

近年来看看一个greenlet死掉时举办点去哪儿。各类greenlet拥有一个父greenlet。父greenlet在各样greenlet先河化时被创立(不过能够在别的时候改变)。父greenlet是当greenlet死掉时,继续原来的任务执行。那样,greenlet就被组织成一棵
树,一级的代码并不在用户创造的 greenlet
中运营,而名称叫主greenlet,约等于树根。

在上头的例证中,gr一和gr贰都以把主greenlet作为父greenlet的。任何2个死掉,执行点都会回去主函数。

未捕获的可怜会涉及到父greenlet。假设下面的 test2()
包涵二个打字与印刷错误(typo),他会转变二个 NameError
而干掉gr二,然后实施点会回到主函数。traceback会展现 test二() 而不是
test一()
。记住,切换不是调用,不过执行点能够在互相的栈容器间并行沟通,而父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,前面边的例子的出口顺序相同。注意 test一()
和 test二() 的参数并不是在 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,然后等着3个不通晓是何人发来的指标,并在此地重临给x。

在意,任何尝试切换来死掉的greenlet的表现都会切换成死掉greenlet的父greenlet,或然父的父,等等。最终的父就是main greenlet,永远不会死掉的。

二.五   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
。依照相当波及规则,有如上边描述的。注意调用这一个情势1致如下:

  def raiser():
    raise typ,val,tb

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

2.6   Greenlet与Python线程

greenlet能够与Python线程1起行使;在这种场所下,每一种线程包括3个独自的
main
greenlet,并具备和谐的greenlet树。分化线程之间不得以并行切换greenlet。

二.七   活动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的衍生产品,它是三个支撑微线程(叫tasklets)的CPython版本。Tasklets运转在伪并发形式下(日常在一个或少于的OS级其他线程),他们通过“channels”来交互数据。

壹方面来说,
3个“greenlet”任然是贰个尚未中间调度的有关微线程的较为原始的定义。换句话说,当你想要在你代码运营时做到规范控制,“greenlet”是1种很有用的章程。在greenlet基础之上,你能够定义本人的微线程调度策略。不管怎样,greenlets也足以以一种尖端控制流结构的不二秘诀用于他们友善。举个例子,大家能够重复生成迭代器。python自带的生成器与greenlet的生成器之间的区分是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表达的是,你不须要选择yield关键词,参见例子:test_generator.py)。

背景

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

一派来说,
3个“greenlet”任然是三个不曾内部调度的有关微线程的比较原始的定义。换句话说,当您想要在您代码运营时形成规范控制,“greenlet”是壹种很有用的秘籍。在greenlet基础之上,你能够定义本人的微线程调度策略。不管怎么样,greenlets也足以以一种尖端控制流结构的不二法门用于他们协调。举个例子,我们得以另行生成迭代器。python自带的生成器与greenlet的生成器之间的区分是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表达的是,你不须要动用yield关键词,参见例子:test_generator.py)。

什么是greenlet

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

greenlet是经过C增加完结的。

例子

我们来思索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程序中,绝一大半的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_轻量级并发程序,使用Python中的greenlet包实现产出编制程序的入门教程。keydown()中被按下的key作为switch()的结果重临给了read_next_char()。

亟需证实的是read_next_char()的挂起与回复都封存其调用堆栈。以便在prprocess_commands()中依照他来的地点苏醒到差别的职位。那使得以1种好的支配流来控制造进程序逻辑成为或者。大家不要完全的重写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    # ignore the command
        process_command(line)

未来,假诺你将次第移植到GUI程序中,绝大部分的GUI成套工具是依据事件驱动的。他们为每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()中根据他来的地方恢复生机到不一致的岗位。那使得以1种好的支配流来控制造进程序逻辑成为或然。大家不必完全的重写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调用,然后就会和事件循环相争执。那种需求出现的景色是足以用102线程来处理,可是大家有更加好的不二等秘书籍,正是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创立3个体协会程,然后调用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(切换)”。

当你创立3个greenlet,它将有一个开端化的空堆栈。当您首先次切换成它,它开头运维2个有血有肉的函数。在那几个函数中或许调用其余的函数,从近年来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()
  • 最后1行跳转到test一, 然后打字与印刷1二,
  • 跳转到test2, 然后打字与印刷5陆
  • 跳转回test壹, 打字与印刷34,
    test1达成,并且gr壹去世。与此同时,程序执行重回到gr一.switch()调用。
  • 急需证明的是7八向来都尚未打字与印刷。

序言

“greenlet”
是小型的独自的伪线程。记挂到作为3个帧堆栈。最远的帧(最后面部分)是你调用的早期的函数,最外侧的帧(最顶层)是在脚下greenlet被压进去的。当你接纳greenlets的时候是经过创造壹多重的这种堆栈,然后在她们之间跳转执行。那种跳转将会导致原先的帧挂起,最终的帧从挂起状态复苏。在greenlets之间的跳转关系叫做“switching(切换)”。

当你创立3个greenlet,它将有二个初叶化的空堆栈。当你首先次切换来它,它开头运营多个现实的函数。在这几个函数中大概调用其余的函数,从当下greenlet中切换出去,等等。当最底部的函数完毕实施,greenlet的栈再一次为空,那时,greenlet病逝。greenlet也可能应三个未捕获的可怜而告1段落。

举个例证:

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()
  • 最后一行跳转到test一, 然后打印1贰,
  • 跳转到test贰, 然后打字与印刷5陆
  • 跳转回test1, 打字与印刷34,
    test一实现,并且gr一驾鹤归西。与此同时,程序执行重临到gr壹.switch()调用。
  • 急需验证的是7捌向来都尚未打字与印刷。

简介

三个协程是3个独门的“假线程”。能够把它想成2个小的帧栈,栈底是你调用的开端函数,栈顶是greenlet当前中断的地点。大家运用协程,实际上就是创办了一体系那样帧栈,然后在它们中间跳转执行。而跳转必须是家弦户诵的,跳转也叫做’switching’。

当您创立贰个协程时,产生三个空的栈,在第1回切换成这些体协会程时,它调用一个优良的函数,那一个函数中得以调用其余函数,能够切换成别的协程等等。当最终栈底函数执行完后,协程的栈变为空,那时候,协程是死的(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()

最后一行切换来test一,打字与印刷1贰,切换来test二,打字与印刷5陆,又切回到test一打字与印刷3四。然后test一甘休,gr一离世。那时候执行回来了gr1.switch()调用。注意到,78并不曾被打字与印刷出。

父级greenlet

让我们看看当greenlet亡故的时候,程序执行到哪个地方去了。每三个greenlet都有一个父级greenlet。最初的父级是创办greenlet的这一个greenlet(父级greenlet是足以在任哪天候被更改)。父级greenlet是当1个greenlet寿终正寝的时候程序继续执行的位置。那种措施,程序组织成一颗树。不在用户创立的greenlet中运作的顶层代码在隐式的主greenlet中运营,它是堆栈数的根。

在地点的例证中,gr一与gr二将主greenlet作为父级greenlet。无论它们中的何人执行完成,程序执行都会回到到”main”greenlet中。

从未捕获的不行将抛出到父级greenlet中。举个例子,假使上面的test贰()包涵七个语法错误,它将生成3个杀掉gr2的NameError错误,那个错误将直接跳转到主greenlet。错误堆栈将呈现test二,而不会是test壹。需求注意的是,switches不是调用,而是程序在互相的”stack
container(堆栈容器)”直接执行的跳转,“parent”定义了逻辑上位居当前greenlet之下的仓库。

父级greenlet

让大家看看当greenlet谢世的时候,程序执行到何地去了。每叁个greenlet都有1个父级greenlet。最初的父级是创办greenlet的那个greenlet(父级greenlet是足以在其他时候被改成)。父级greenlet是当3个greenlet病逝的时候程序继续执行的地点。那种格局,程序组织成壹颗树。不在用户成立的greenlet中运作的顶层代码在隐式的主greenlet中运转,它是堆栈数的根。

在地方的例证中,gr一与gr2将主greenlet作为父级greenlet。无论它们中的什么人执行完成,程序执行都会重临到”main”greenlet中。

尚无捕获的卓殊将抛出到父级greenlet中。举个例子,即使地方的test2()包罗八个语法错误,它将生成3个杀掉gr二的NameError错误,那个错误将一直跳转到主greenlet。错误堆栈将呈现test2,而不会是test壹。供给留意的是,switches不是调用,而是程序在相互的”stack
container(堆栈容器)”直接实施的跳转,“parent”定义了逻辑上位居当前greenlet之下的堆栈。

父协程

各种体协会程都有一个父协程。协程在哪些协程中被成立,那么那一个协程便是父协程,当然后边能够变动。当有些协程身故后,会在父协程中继续执行。举个例子,在g第11中学创建了g2,那么g1就是g贰的父协程,g二去世后,会在g1中继续执行。这么说的话,协程是树结构的。最上层的代码不是运营在用户定义的协程中,而是在贰个隐式的主协程中,它是协程树的根(root)。

在上头的例子中,gr1和gr贰的父协程都以主协程。不管哪1个寿终正寝,执行都会回来主协程。

老大也会被传到父协程。比如说,test2中若包罗了三个’typo’,就会引发NameError分外,然后杀死gr2,执行会直接重回主协程。Traceback会显示test二而不是test壹。注意,协程的切换不是调用,而是在平行的”栈容器”中传送执行。

实例化对象

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

  • greenlet(run=None,parent=None):创设三个新的greenlet对象(还未曾先河运转)。run是二个可调用的函数,用来被调用。parent定义父级greenlet,暗中同意是现阶段greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:这么些新鲜的百般不会抛出到父级greenlet中,那足以用来杀死三个单一的greenlet。

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

实例化对象

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

  • greenlet(run=None,parent=None):创建一个新的greenlet对象(还尚未开首运营)。run是3个可调用的函数,用来被调用。parent定义父级greenlet,私下认可是现阶段greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:那个新鲜的丰硕不会抛出到父级greenlet中,那足以用来杀死两个单纯的greenlet。

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

协程类

greenlet.greenlet就是协程类,它扶助下边壹些操作:

切换

当在叁个greenlet中调用方法switch(),在greenlet之间的切换将生出,不荒谬情状下,程序执行跳转到switch()被调用的greenlet中。只怕当一个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”与4二。多说一句,test壹(),test二()的参数不是在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,它是尚未会死掉的)。

切换

当在3个greenlet中调用方法switch(),在greenlet之间的切换将生出,正常情况下,程序执行跳转到switch()被调用的greenlet中。可能当3个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")

已与事先例子1样顺序执行,它将会打字与印刷“hello
world”与4二。多说一句,test一(),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变量。

提示一下,任何准备切换来2个死去的greenlet的将会走到已谢世greenlet的父级,恐怕父级的父级,以此类推(末了的父级是“main”
greenlet,它是未曾会死掉的)。

greenlet(run=None, parent=None)

创设3个新的协程对象。run是1个可调用对象,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执行,可是及时抛出一个加以的丰裕。假使未有参数提供,暗中同意非凡是greenlet.格林letExit。同地点描述壹样,寻常的那多少个传递规则生效。调用该措施同上边代码是差不离相当于的:

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

    有有个别两样的是,这段代码无法用于greenlet.格林letExit格外,这一个卓殊将不会从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。同地点描述1样,寻常的百般传递规则生效。调用该办法同上边代码是大致等于的:

      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个线程包括一个独立的带有3个子greenlets树的“main”
greenlet。混合或切换在区别线程中的greenlets是不容许的事务。

Greenlets与python的线程

格林lets将得以和python线程结合起来。那种情景下,每二个线程包罗二个独自的蕴藏八个子greenlets树的“main”
greenlet。混合或切换在不相同线程中的greenlets是不大概的事体。

greenlet.GreenletExit

以此越发的那多少个不会传给父协程,常用来杀死协程。

greenlet是足以被持续的。协程通过执行run属性来运营。在子类中,能够随便地去定义run,而不是迟早要传送run参数给构造器。

greenlets的排放物回收生命周期

比方对3个greenlet的兼具涉嫌都已经失效(包蕴来自其余greenlets中的父级属性的关联),那时候,未有其余一种艺术切换回该greenlet中。那种状态下,格林letExit相当将会爆发。那是1个greenlet接受异步执行的唯1格局。使用try:finally:语句块来清理被那几个greenlet使用的财富。那种天性协理壹种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的末梢关联失效,那种循环将机关终止。

若是greenlet要么回老家,要么依据留存有些地方的涉及苏醒。只要求捕获与忽略可能造成极端循环的格林letExit。

格林lets不插足垃圾回收。循环那个在greenlet框架中的数据时候,那一个多上校不会被检查测试到。循环的存款和储蓄别的greenlets的引用将只怕引致内部存款和储蓄器泄漏。

greenlets的废品回收生命周期

只要对八个greenlet的具有涉及都早就失效(包括来自别的greenlets中的父级属性的涉及),那时候,没有任何1种办法切换回该greenlet中。那种情形下,格林letExit万分将会产生。这是二个greenlet接受异步执行的绝无仅有格局。使用try:finally:语句块来清理被那几个greenlet使用的财富。那种性质援救一种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的末段关联失效,那种循环将电动结束。

若果greenlet要么回老家,要么依照留存某些地点的涉嫌复苏。只必要捕获与忽视恐怕造成极端循环的格林letExit。

Greenlets不参与垃圾回收。循环那几个在greenlet框架中的数据时候,这一个数据将不会被检验到。循环的蕴藏其余greenlets的引用将也许引致内部存储器泄漏。

切换

有三种状态会生出协程之间的切换。壹是有个别体协会程主动调用switch方法,那种景观下会切换来被调用的协程中。2是协程离世,那时协程会切换成父协程。在切换时,1个对象或越发被传送到目的协程。那用来在协程之间传递音讯。如下边那一个事例:

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”和4二。注意到,test一和test二在协程创造时并从未提供参数,而是在第一遍切换的地点。

错误堆栈帮助

当使用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()相似的新的轩然大波。

荒唐堆栈辅助

当使用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,这种景观下会一向重临到主协程)。

假如切换成2个已经逝去的的协程,那么实际上是切换来它的父协程,依次类推。

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

3个可调用对象,当g初阶实践时,调用它。可是壹旦起首进行后,这么些特性就不存在了。

关联

  • PyGreenlet_Import():三个宏定义,导入greenlet模块,伊始化C
    API。必须在每一个用到greenlet C API的模块中调用三回。
  • int PyGreenlet_Check(PyObject
    *p):三个宏定义,倘若参数是Py格林let再次来到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为设置成功,-壹,表示g不是一卓有效用的Py格林let指针,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():1个宏定义,导入greenlet模块,初阶化C
    API。必须在每一个用到greenlet C API的模块中调用叁回。
  • int PyGreenlet_Check(PyObject
    *p):二个宏定义,假若参数是Py格林let重回true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):一个宏定义,假如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应该是3个tuple,kwargs应该是2个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)

1经协程处于活跃状态,则为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线程

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

垃圾堆收集

若是对一个体协会程的引用计数为0,那么就不能另行切换成这几个体协会程。那种气象下,协程会发出八个格林letExit分外。这是协程唯壹1种异步接收到GreenletExit万分的情事。能够用try…finally…来解决协程的能源。那脾性子允许大家用最为循环的方法来等待数据并拍卖,因为当协程的引用计数变成0时,循环会自动刹车。

在最棒循环中,假设想要协程驾鹤归西就抓获格林letExit非常。倘使想拥有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地图