英文原稿地址:
普通话翻译转发地址:

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

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

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

1   动机

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

3个”greenlet”,是1个更是原始的微线程的概念,然则尚未调度,或然叫做协程。那在你供给调控你的代码时很有用。你可以协调组织微线程的
调度器;也得以应用”greenlet”完结高等的调节流。例如能够重复创建构造器;不一样于Python的构造器,我们的构造器能够嵌套的调用函数,而被
嵌套的函数也能够 yield
多个值。(其余,你并不必要三个”yield”关键字,参考例子)。

格林let是用作二个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”
是2个十分的小的独自微线程。能够把它想像成一个仓库帧,栈底是开端调用,而栈顶是当前greenlet的刹车地点。你利用greenlet创造一群那样的仓库,然后在他们中间跳转施行。跳转不是纯属的:二个greenlet必须选拔跳转到选用好的另一个greenlet,那会让前一个挂起,而后2个回复。两个greenlet之间的跳转称为 切换(switch) 。

当您成立2个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() ,它打字与印刷1二,然后跳转到 test二()
,打字与印刷5陆,然后跳转回 test壹() ,打字与印刷3四,然后 test一()
就甘休,gr壹死掉。那时实施会回来原先的 gr壹.switch()
调用。注意,7八是不会被打字与印刷的。

2.2   父greenlet

今昔看望一个greenlet死掉时实行点去啥地方。种种greenlet拥有多少个父greenlet。父greenlet在各种greenlet初阶化时被创设(不过能够在别的时候改造)。父greenlet是当greenlet死掉时,继续原来的职位执行。那样,greenlet就被集体成1棵
树,一流的代码并不在用户创造的 greenlet
中运转,而名称为主greenlet,也便是树根。

在上头的例子中,gr1和gr2都是把主greenlet作为父greenlet的。任何3个死掉,试行点都会再次来到主函数。

未捕获的要命会涉嫌到父greenlet。假诺上面的 test二()
包罗四个打字与印刷错误(typo),他会转移贰个 NameError
而干掉gr二,然后实行点会回到主函数。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,它用于干掉3个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,恒久不会死掉的。

二.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。

二.七   活动greenlet的污物收罗

假使不再有对greenlet对象的引用时(包蕴别的greenlet的parent),依然尚未艺术切换回greenlet。那种情景下会变换一个格林letExit
非凡到greenlet。那是greenlet收到异步分外的绝无仅有情状。应该交由3个 try ..
finally
用于清理greenlet内的能源。这么些作用并且同意greenlet中最为循环的编制程序风格。那样循环能够在结尾一个引用消失时自动刹车。

比方不希望greenlet死掉恐怕把引用放到别处,只要求捕捉和忽视 格林letExit
非常就能够。

greenlet不参加垃圾搜集;greenlet帧的大循环引用数据会被检查评定到。将引用传递到任何的循环greenlet会唤起内部存款和储蓄器败露。

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

翻译自官方文书档案greenlet。

背景

金沙注册送58 ,greenlet包是Stackless的衍生产品,它是二个支撑微线程(叫tasklets)的CPython版本。Tasklets运营在伪并发情势下(经常在七个或零星的OS等第的线程),他们经过“channels”来交互数据。

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

背景

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

单一贯讲,
2个“greenlet”任然是三个从未内部调度的关于微线程的相比原始的定义。换句话说,当您想要在您代码运维时做到规范调控,“greenlet”是1种很有用的章程。在greenlet基础之上,你可以定义自个儿的微线程调度攻略。不管如何,greenlets也足以以1种尖端调节流结构的不二等秘书籍用于他们协调。举个例子,我们可以重复生成迭代器。python自带的生成器与greenlet的生成器之间的界别是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表明的是,你不必要利用yield关键词,参见例子:test_generator.py)。

什么是greenlet

greenlet是从Stackless中分离的连串。greenlet也叫微线程、协程,它的调度是由程序鲜明调控的,所以举行流程是原则性的、分明的。而线程的调度完全交由操作系统,实行种种不能预想。同时,协程之间切换的代价远比线程小。

greenlet是透过C增加完毕的。

例子

我们来设想1个用户输入指令的终端调节台系统。假若输入是每一种字符输入。在如此的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    # 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是另1种不须求关联锁与从不当机难题的可选的消除方案。你施行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(),将其改动为状态机。

例子

咱俩来设想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程序中,绝超越2/肆的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
    greenlet1局地的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    # 忽略当前的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,它将有3个起初化的空旅舍。当你首先次切换来它,它先河运营二个现实的函数。在那几个函数中可能调用其余的函数,从当前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()
  • 末段壹行跳转到test一, 然后打字与印刷1贰,
  • 跳转到test2, 然后打字与印刷56
  • 跳转回test一, 打印3四,
    test一完成,并且gr一寿终正寝。与此同时,程序实行重回到gr一.switch()调用。
  • 亟需表达的是7八一直都不曾打字与印刷。

序言

“greenlet”
是小型的单身的伪线程。思考到作为四个帧货仓。最远的帧(最尾部)是您调用的初期的函数,最外面包车型地铁帧(最顶层)是在时下greenlet被压进去的。当您选用greenlets的时候是透过创立一名目许多的那种货仓,然后在她们之间跳转实践。那种跳转将会导致原先的帧挂起,最后的帧从挂起状态回涨。在greenlets之间的跳转关系叫做“switching(切换)”。

当您创立二个greenlet,它将有三个起初化的空仓库。当你首先次切换来它,它开端运转二个切实可行的函数。在这一个函数中或许调用别的的函数,从近日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()
  • 聊起底一行跳转到test壹, 然后打字与印刷1二,
  • 跳转到test贰, 然后打字与印刷5陆
  • 跳转回test一, 打字与印刷3四,
    test一完结,并且gr一与世长辞。与此同时,程序试行重返到gr一.switch()调用。
  • 急需注解的是7八平昔都不曾打字与印刷。

简介

二个体协会程是二个独立的“假线程”。能够把它想成二个小的帧栈,栈底是你调用的初阶函数,栈顶是greenlet当前半上落下的地点。大家应用协程,实际上正是成立了一密密麻麻那样帧栈,然后在它们之间跳转试行。而跳转必须是显然的,跳转也号称’switching’。

当你创建2个体协会程时,爆发1个空的栈,在第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()

最终一行切换到test一,打印1二,切换来test2,打字与印刷5陆,又切回到test1打字与印刷3④。然后test一截止,gr壹驾鹤归西。那时候施行回来了gr1.switch()调用。注意到,78并从未被打字与印刷出。

父级greenlet

让大家看看当greenlet寿终正寝的时候,程序试行到哪个地方去了。每3个greenlet都有三个父级greenlet。最初的父级是创造greenlet的那些greenlet(父级greenlet是足以在此外时候被改造)。父级greenlet是当二个greenlet与世长辞的时候程序继续施行的地点。那种措施,程序协会成一颗树。不在用户创造的greenlet中运作的顶层代码在隐式的主greenlet中运作,它是仓库数的根。

在上头的例子中,gr一与gr2将主greenlet作为父级greenlet。无论它们中的哪个人试行落成,程序实践都会回去到”main”greenlet中。

尚未捕获的不胜将抛出到父级greenlet中。举个例子,假使上面包车型大巴test2()包涵多个语法错误,它将生成1个杀死gr二的NameError错误,这几个错误将直接跳转到主greenlet。错误宾馆将展现test二,而不会是test1。需求留意的是,switches不是调用,而是程序在相互的”stack
container(货仓容器)”直接推行的跳转,“parent”定义了逻辑上位居当前greenlet之下的旅社。

父级greenlet

让我们看看当greenlet寿终正寝的时候,程序实施到何地去了。每2个greenlet都有一个父级greenlet。最初的父级是创设greenlet的这多少个greenlet(父级greenlet是能够在别的时候被退换)。父级greenlet是当四个greenlet死亡的时候程序继续施行的地点。那种措施,程序组织成一颗树。不在用户创造的greenlet中运作的顶层代码在隐式的主greenlet中运营,它是货仓数的根。

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

并未捕获的不行将抛出到父级greenlet中。举个例子,假如地点的test二()包括三个语法错误,它将生成一个干掉gr二的NameError错误,这几个荒唐将一向跳转到主greenlet。错误仓库将呈现test2,而不会是test一。供给专注的是,switches不是调用,而是程序在互动的”stack
container(旅社容器)”直接实施的跳转,“parent”定义了逻辑上位居当前greenlet之下的酒馆。

父协程

每一种体协会程都有三个父协程。协程在哪些协程中被创制,那么这一个体协会程便是父协程,当然后边能够转移。当有个别体协会程与世长辞后,会在父协程中继续推行。举个例子,在g第11中学开创了g2,那么g一正是g贰的父协程,g2谢世后,会在g第11中学继续实行。这么说的话,协程是树结构的。最上层的代码不是运作在用户定义的协程中,而是在一个隐式的主协程中,它是协程树的根(root)。

在下边包车型地铁例子中,gr一和gr二的父协程都以主协程。不管哪三个凋谢,实践都会回去主协程。

可怜也会被传到父协程。比如说,test第22中学若包蕴了贰个’typo’,就会吸引NameError卓殊,然后杀死gr2,推行会一贯回到主协程。Traceback会彰显test二而不是test一。注意,协程的切换不是调用,而是在平行的”栈容器”中传送执行。

实例化对象

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

  • greenlet(run=None,parent=None):成立三个新的greenlet对象(还未有开端运维)。run是2个可调用的函数,用来被调用。parent定义父级greenlet,私下认可是近年来greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:那个特殊的那三个不会抛出到父级greenlet中,那能够用来杀死2个纯粹的greenlet。

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

实例化对象

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

  • greenlet(run=None,parent=None):创造三个新的greenlet对象(还未曾伊始运转)。run是2个可调用的函数,用来被调用。parent定义父级greenlet,暗中认可是现阶段greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.GreenletExit:那一个新鲜的百般不会抛出到父级greenlet中,这足以用来杀死1个纯粹的greenlet。

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

协程类

greenlet.greenlet即是协程类,它协理下边一些操作:

切换

当在八个greenlet中调用方法switch(),在greenlet之间的切换将生出,没有难题情形下,程序推行跳转到switch()被调用的greenlet中。或许当一个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”与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变量。

唤醒一下,任何准备切换来1个回老家的greenlet的将会走到已去世greenlet的父级,大概父级的父级,就这样推算(最后的父级是“main”
greenlet,它是从未会死掉的)。

切换

当在贰个greenlet中调用方法switch(),在greenlet之间的切换将发生,符合规律情形下,程序施行跳转到switch()被调用的greenlet中。或许当二个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(*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)

创设贰个新的协程对象。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实践,可是及时抛出二个加以的非常。固然未有参数提供,暗许极度是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实行,可是及时抛出一个加以的那么些。要是未有参数提供,私下认可非常是greenlet.格林letExit。同地点描述同样,正常的不行传递规则生效。调用该方法同上边代码是大概等于的:

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

    有好几不1的是,那段代码无法用于greenlet.格林letExit格外,那个那么些将不会从g_raiser传播到g。

greenlet.getcurrent()

再次来到当前协程,也正是调用那一个函数的协程。

Greenlets与python的线程

格林lets将得以和python线程结合起来。那种情况下,每一个线程包罗二个单独的隐含八个子greenlets树的“main”
greenlet。混合或切换在区别线程中的greenlets是不恐怕的政工。

Greenlets与python的线程

格林lets将得以和python线程结合起来。这种情形下,每贰个线程包蕴三个单身的盈盈1个子greenlets树的“main”
greenlet。混合或切换在不一致线程中的greenlets是不容许的政工。

greenlet.GreenletExit

其一特殊的那多少个不会传给父协程,常用来杀死协程。

greenlet是足以被连续的。协程通过试行run属性来运维。在子类中,能够随心所欲地去定义run,而不是任天由命要传送run参数给构造器。

greenlets的废品回收生命周期

假若对一个greenlet的有所涉嫌都曾经失效(包含来自另外greenlets中的父级属性的关系),那时候,未有其余1种艺术切换回该greenlet中。这种景观下,格林letExit非凡将会发出。那是一个greenlet接受异步实践的绝无仅有办法。使用try:finally:语句块来清理被这一个greenlet使用的财富。那种属性帮衬一种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的最后关联失效,那种循环将机关终止。

若是greenlet要么回老家,要么根据留存有些地点的关系恢复生机。只要求捕获与忽略恐怕变成极端循环的格林letExit。

格林lets不出席垃圾回收。循环那几个在greenlet框架中的数据时候,那些数据将不会被质量评定到。循环的囤积别的greenlets的引用将只怕导致内部存款和储蓄器泄漏。

greenlets的排放物回收生命周期

即便对1个greenlet的具有涉及都早就失效(包涵来自别的greenlets中的父级属性的涉及),那时候,未有其余1种办法切换回该greenlet中。这种气象下,格林letExit格外将会发出。那是二个greenlet接受异步实行的绝无仅有办法。使用try:finally:语句块来清理被这么些greenlet使用的财富。那种性质帮忙一种编制程序风格,greenlet无限循环等待数据同时实施。当对该greenlet的尾声关联失效,那种循环将机关结束。

只要greenlet要么回老家,要么依照留存有些地点的关联苏醒。只须要捕获与忽视恐怕导致极端循环的格林letExit。

格林lets不插足垃圾回收。循环那个在greenlet框架中的数据时候,那些数据将不会被检查实验到。循环的蕴藏其余greenlets的引用将也许引致内部存款和储蓄器泄漏。

切换

有两种情状会产生协程之间的切换。1是有个别体协会程主动调用switch方法,那种景观下会切换成被调用的协程中。二是协程离世,那时协程会切换来父协程。在切换时,3个目标或尤其被传送到对象协程。那用来在协程之间传递音讯。如下边那个例子:

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和test②在协程创立时并从未提供参数,而是在第1遍切换的地点。

不当仓库援救

当使用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切换是1件很拮据的事务。因而,为了千锤百炼对greenlet基础代码的调控,错误客栈,难题讲述的协理,在greenlet模块中,有壹对新的主意:

  • greenlet.gettrace():重临先前已某个调用旅舍方法,或然None。
  • greenlet.settrace(callback):设置3个新的调用仓库方法,重返早先时期已部分艺术或然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模块的1体化的API访问。

C API 相关

格林lets能够经过用C/C++写的扩展模块来变化与保障,或许来自于嵌入到python中的应用。greenlet.h
头文件被提供,用来呈现对原生的python模块的1体化的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。必须在每2个用到greenlet C API的模块中调用二次。
  • int PyGreenlet_Check(PyObject
    *p):三个宏定义,假诺参数是Py格林let重回true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):1个宏定义,假使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为设置成功,-一,表示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,一个空的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。必须在每多少个用到greenlet C API的模块中调用叁次。
  • int PyGreenlet_Check(PyObject
    *p):2个宏定义,假使参数是Py格林let重返true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):一个宏定义,如若greenlet在起来了回去true。
  • int PyGreenlet_ACTIVE(PyGreenlet
    *g):三个宏定义,借使greenlet在运动中回到true。
  • PyGreenlet *PyGreenlet_GET_PARENT(PyGreenlet
    *g):1个宏定义,再次回到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成立二个新的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

1旦协程已气绝身亡,那么值是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卓殊。这是协程唯1一种异步接收到格林letExit相当的状态。能够用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地图