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

英文原版的书文地址:
汉语翻译转发地址:

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

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

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

1   动机

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

3个”greenlet”,是叁个更是原始的微线程的概念,不过从未调度,只怕叫做协程。那在你要求控制你的代码时很有用。你可以协调组织微线程的
调度器;也得以应用”greenlet”达成高级的控制流。例如能够重复成立构造器;差异于Python的构造器,大家的构造器能够嵌套的调用函数,而被
嵌套的函数也得以 yield
1个值。(其它,你并不要求四个”yield”关键字,参考例子)。

Greenlet是当做二个C扩大模块给未修改的解释器的。

1.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  #忽略指令
    process_commands(line)

现行一经你要把程序移植到GUI,而当先二分一GUI是事件驱动的。他们会在历次的用户输入时调用回调函数。那种情景下,就很难落到实处read_next_char() 函数。大家有五个不匹配的函数:

轻量级并发程序,使用Python中的greenlet包实现产出编程的入门教程。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   简介

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

当您创立1个greenlet,它拿走一个开端化过的空堆栈;当您首先次切换成它,他会运营钦点的函数,然后切换跳出greenlet。当最终栈底
函数结束时,greenlet的堆栈又编制程序空的了,而greenlet也就死掉了。greenlet也会因为2个未捕捉的丰盛死掉。

例如:

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

谈到底壹行跳转到 test一() ,它打字与印刷1二,然后跳转到 test2()
,打字与印刷56,然后跳转回 test一() ,打字与印刷3肆,然后 test1()
就终止,gr1死掉。那时执行会回来原先的 gr一.switch()
调用。注意,78是不会被打字与印刷的。

2.2   父greenlet

现行反革命看望一个greenlet死掉时实施点去哪个地方。每种greenlet拥有三个父greenlet。父greenlet在种种greenlet伊始化时被创立(但是能够在别的时候改变)。父greenlet是当greenlet死掉时,继续原来的职位执行。那样,greenlet就被组织成1棵
树,一流的代码并不在用户创立的 greenlet
中运维,而名叫主greenlet,相当于树根。

在上边的事例中,gr壹和gr二都是把主greenlet作为父greenlet的。任何1个死掉,执行点都会重临主函数。

未捕获的可怜会涉嫌到父greenlet。假使上边的 test二()
包涵三个打字与印刷错误(typo),他会转变2个 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那里去。在切换时,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一()
和 test2() 的参数并不是在 greenlet
创设时内定的,而是在第2遍切换成那里时传递的。

那里是纯粹的调用方式:

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,永远不会死掉的。

二.伍   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.GreenletExit
。依照相当波及规则,有如上边描述的。注意调用这么些法子壹致如下:

  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。

二.7   活动greenlet的排放物收集

一旦不再有对greenlet对象的引用时(包涵其余greenlet的parent),依然未有主意切换回greenlet。那种情状下会转移一个格林letExit
很是到greenlet。那是greenlet收到异步分外的绝无仅有情形。应该提交二个 try ..
finally
用于清理greenlet内的能源。这一个作用并且同意greenlet中格外循环的编制程序风格。那样循环能够在最后2个引用消失时自动刹车。

借使不指望greenlet死掉大概把引用放到别处,只供给捕捉和马虎 格林letExit
极度即可。

greenlet不出席垃圾收集;greenlet帧的循环引用数据会被检查测试到。将引用传递到别的的循环greenlet会滋生内部存款和储蓄器败露。

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

翻译自官方文书档案greenlet。

背景

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

另一方面来说,
三个“greenlet”任然是一个从未有过中间调度的关于微线程的较为原始的概念。换句话说,当您想要在你代码运营时成功准确控制,“greenlet”是1种很有用的法子。在greenlet基础之上,你能够定义本人的微线程调度策略。不管如何,greenlets也能够以壹种尖端控制流结构的主意用于他们友善。举个例子,大家得以重新生成迭代器。python自带的生成器与greenlet的生成器之间的分别是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表达的是,你不要求选取yield关键词,参见例子:test_generator.py)。

背景

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

另壹方面来说,
一个“greenlet”任然是2个尚未中间调度的关于微线程的相比原始的概念。换句话说,当您想要在您代码运行时形成准确控制,“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程序中,绝超越33.33%的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
    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(),将其更换为状态机。

例子

咱俩来设想多少个用户输入指令的终极控制台系统。要是输入是各个字符输入。在那样的二个系统中,有个独立的大循环如下所示:

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是另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
    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(),将其转移为状态机。

示例

有那般二个系统,它根据用户在终点输入指令的两样而推行不一致的操作,即使输入是逐字符的。部分代码可能是这么的:

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库都以事件驱动的,每当用户输入都会调用1个回调函数去处理。在那种情景下,假若还想用上边包车型大巴代码逻辑,恐怕是这么的:

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始建2个协程,然后调用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,它将有一个开首化的空堆栈。当您首先次切换成它,它初阶运营一个具体的函数。在那一个函数中恐怕调用别的的函数,从此时此刻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二,
  • 跳转到test二, 然后打字与印刷56
  • 跳转回test1, 打字与印刷3肆,
    test壹完毕,并且gr1驾鹤归西。与此同时,程序执行再次来到到gr壹.switch()调用。
  • 亟待验证的是7八一贯都并未有打字与印刷。

序言

“greenlet”
是微型的独自的伪线程。考虑到作为二个帧堆栈。最远的帧(最尾部)是您调用的最初的函数,最外面包车型地铁帧(最顶层)是在最近greenlet被压进去的。当您使用greenlets的时候是经过创办1密密麻麻的那种堆栈,然后在他们之间跳转执行。那种跳转将会造成原先的帧挂起,最后的帧从挂起状态苏醒。在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八平昔都不曾打字与印刷。

简介

三个体协会程是1个单独的“假线程”。能够把它想成一个小的帧栈,栈底是你调用的初阶函数,栈顶是greenlet当前中断的地方。我们应用协程,实际上正是创制了一密密麻麻那样帧栈,然后在它们中间跳转执行。而跳转必须是明摆着的,跳转也称之为’switching’。

当您创设一个体协会程时,爆发一个空的栈,在首次切换来这一个体协会程时,它调用2个独特的函数,那些函数中能够调用其余函数,可以切换成其余协程等等。当最后栈底函数执行完后,协程的栈变为空,那时候,协程是死的(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陆,又切回到test1打字与印刷3四。然后test一停止,gr壹过逝。那时候执行回来了gr1.switch()调用。注意到,7八并不曾被打字与印刷出。

父级greenlet

让我们看看当greenlet身故的时候,程序执行到哪个地方去了。每叁个greenlet都有3个父级greenlet。最初的父级是创建greenlet的这八个greenlet(父级greenlet是足以在其余时候被改变)。父级greenlet是当三个greenlet身故的时候程序继续执行的地点。那种措施,程序组织成1颗树。不在用户创建的greenlet中运作的顶层代码在隐式的主greenlet中运作,它是堆栈数的根。

在上头的例子中,gr一与gr2将主greenlet作为父级greenlet。无论它们中的谁执行达成,程序执行都会重回到”main”greenlet中。

从不捕获的越发将抛出到父级greenlet中。举个例子,如果地点的test二()包蕴3个语法错误,它将生成一个杀掉gr二的NameError错误,那个错误将一贯跳转到主greenlet。错误堆栈将展现test二,而不会是test一。须要专注的是,switches不是调用,而是程序在相互的”stack
container(堆栈容器)”直接执行的跳转,“parent”定义了逻辑上位居当前greenlet之下的堆栈。

父级greenlet

让大家看看当greenlet去世的时候,程序执行到何地去了。每叁个greenlet都有三个父级greenlet。最初的父级是创设greenlet的那些greenlet(父级greenlet是足以在别的时候被改变)。父级greenlet是当三个greenlet病逝的时候程序继续执行的地点。那种办法,程序协会成壹颗树。不在用户制造的greenlet中运作的顶层代码在隐式的主greenlet中运作,它是堆栈数的根。

在上边的事例中,gr一与gr二将主greenlet作为父级greenlet。无论它们中的谁执行完成,程序执行都会再次回到到”main”greenlet中。

从未有过捕获的万分将抛出到父级greenlet中。举个例子,假诺上边的test2()包涵3个语法错误,它将生成3个杀掉gr二的NameError错误,那个张冠李戴将直接跳转到主greenlet。错误堆栈将展现test2,而不会是test一。须要专注的是,switches不是调用,而是程序在交互的”stack
container(堆栈容器)”直接实施的跳转,“parent”定义了逻辑上位居当前greenlet之下的库房。

父协程

各种体协会程都有2个父协程。协程在哪个体协会程中被制造,那么这几个体协会程正是父协程,当然前面能够转移。当有些体协会程离世后,会在父协程中继续执行。举个例子,在g第11中学创制了g2,那么g1正是g2的父协程,g二长逝后,会在g第11中学继续执行。这么说的话,协程是树结构的。最上层的代码不是运营在用户定义的协程中,而是在2个隐式的主协程中,它是协程树的根(root)。

在上边的事例中,gr一和gr贰的父协程都以主协程。不管哪1个已去世,执行都会重临主协程。

非凡也会被传到父协程。比如说,test第22中学若包罗了二个’typo’,就会掀起NameError分外,然后杀死gr二,执行会一向回到主协程。Traceback会呈现test2而不是test壹。注意,协程的切换不是调用,而是在平行的”栈容器”中传送执行。

实例化对象

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

  • greenlet(run=None,parent=None):创设八个新的greenlet对象(还不曾开首运维)。run是八个可调用的函数,用来被调用。parent定义父级greenlet,暗中认可是日前greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:那几个尤其的相当不会抛出到父级greenlet中,那足以用来杀死3个纯净的greenlet。

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

实例化对象

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

  • greenlet(run=None,parent=None):创造3个新的greenlet对象(还从未伊始运维)。run是3个可调用的函数,用来被调用。parent定义父级greenlet,暗中同意是时下greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:这些奇异的不行不会抛出到父级greenlet中,那能够用来杀死一个纯净的greenlet。

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

协程类

greenlet.greenlet正是协程类,它扶助上面1些操作:

切换

当在二个greenlet中调用方法switch(),在greenlet之间的切换将生出,符合规律意况下,程序执行跳转到switch()被调用的greenlet中。可能当三个greenlet与世长辞,程序执行将跳转到父级greenlet程序中,当爆发切换的时候,贰个对象或一个更加被发送到指标greenlet中。那是1种在四个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,发送数据,作为1个奇异的例证,假若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中。那是1种在七个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壹(),test2()的参数不是在greenlet创立的时候给的,而是在第二次切换的时候给出。

此间给出了关于发送的数码的彰着的规则:

g.switch(*args, **kwargs):切换执行到greenlet
g,发送数据,作为3个非同小可的例子,假诺g未有实施,它将启幕进行。

对此将死的greenlet。当run()实现的时候,将会时有发生对象给父级greenlet。假使greenlet因为那多少个而告1段落,这一个分外将会抛出到父级greenlet中(greenlet.格林letExit例外,这几个特别被破获了还要平素退出到父级greenlet中)。

除开上边例子描述的,平日目的greenlet(父级greenlet)接收此前调用switch()挂起,执行实现重返的重回值作为结果。事实上,纵然对switch()的调用不会即时赶回结果,不过当其余一些greenlet切换回来的时候,在明天的有个别点将会再次回到结果。当切换爆发的时候,程序将在它后边挂起的地点恢复生机。switch()本人回去产生的目的。那就意味着x=g.switch(y)将y给g,稍后将重回从某些不涉及的greenlet中回到的不关乎的指标给x变量。

升迁一下,任何准备切换成3个谢世的greenlet的将会走到病逝greenlet的父级,只怕父级的父级,以此类推(最后的父级是“main”
greenlet,它是未曾会死掉的)。

greenlet(run=None, parent=None)

创造3个新的协程对象。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执行,不过及时抛出2个加以的尤其。假使未有参数提供,默许万分是greenlet.格林letExit。同地点描述1样,正常的优秀传递规则生效。调用该措施同上面代码是差不多约等于的:

      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.GreenletExit。同地点描述壹样,符合规律的可怜传递规则生效。调用该格局同下边代码是大概等于的:

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

    有好几比不上的是,那段代码不能够用于greenlet.格林letExit格外,这几个那么些将不会从g_raiser传播到g。

greenlet.getcurrent()

归来当前协程,相当于调用这么些函数的协程。

Greenlets与python的线程

Greenlets将得以和python线程结合起来。那种情景下,每二个线程包括3个单独的涵盖3个子greenlets树的“main”
greenlet。混合或切换在不相同线程中的greenlets是不也许的作业。

Greenlets与python的线程

格林lets将得以和python线程结合起来。那种情景下,每3个线程包罗一个独门的涵盖3个子greenlets树的“main”
greenlet。混合或切换在分歧线程中的greenlets是不容许的作业。

greenlet.GreenletExit

其壹奇异的不行不会传给父协程,常用来杀死协程。

greenlet是足以被三番七次的。协程通过执行run属性来运作。在子类中,可以任意地去定义run,而不是自然要传递run参数给构造器。

greenlets的污物回收生命周期

比方对1个greenlet的具备涉及都早已失效(包含来自其余greenlets中的父级属性的涉及),这时候,未有此外一种艺术切换回该greenlet中。那种状态下,格林letExit至极将会发生。那是八个greenlet接受异步执行的绝无仅有办法。使用try:finally:语句块来清理被那几个greenlet使用的能源。那种天性援助一种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的尾声关联失效,那种循环将活动截至。

1旦greenlet要么回老家,要么依照留存有些地点的关系复苏。只须求捕获与忽略或许导致极端循环的格林letExit。

Greenlets不参加垃圾回收。循环那叁个在greenlet框架中的数据时候,这个数据将不会被检查评定到。循环的囤积其余greenlets的引用将大概引致内存泄漏。

greenlets的废物回收生命周期

一经对三个greenlet的持有关乎都早就失效(包罗来自其余greenlets中的父级属性的涉及),那时候,未有任何1种办法切换回该greenlet中。那种气象下,格林letExit非常将会爆发。那是三个greenlet接受异步执行的唯壹方法。使用try:finally:语句块来清理被这几个greenlet使用的能源。那种属性辅助1种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的最后关联失效,那种循环将机关终止。

万一greenlet要么回老家,要么依据留存某些地点的关系苏醒。只须要捕获与忽略或许造成极端循环的格林letExit。

格林lets不参与垃圾回收。循环那么些在greenlet框架中的数据时候,这个数量将不会被检查测试到。循环的积存别的greenlets的引用将恐怕引致内部存款和储蓄器泄漏。

切换

有二种情形会发出协程之间的切换。一是某些体协会程主动调用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。注意到,test壹和test二在协程创造时并未提供参数,而是在首先次切换的地方。

谬误堆栈接济

当使用greenlet的时候,标准的python错误堆栈与叙述将不会依照预期的运转,因为堆栈与框架的切换爆发在平等的线程中。使用守旧的法门可信赖的质量评定greenlet切换是壹件很拮据的事务。由此,为了一字不苟对greenlet基础代码的调节,错误堆栈,难点讲述的匡助,在greenlet模块中,有一部分新的不二等秘书诀:

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

      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。当1些事件产生时,这几个回调函数被调用,能够永安里做一下非确定性信号处理。

      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,那种情状下会直接重临到主协程)。

假若切换来一个已放手人寰的的协程,那么实际上是切换成它的父协程,依次类推。

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

二个可调用对象,当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):2个宏定义,再次回到greenlet中的父级greenlet。
  • int PyGreenlet_SetParent(PyGreenlet *g, PyGreenlet
    *nparent):设置父级greenlet。重临0为设置成功,-1,表示g不是1卓有功用的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,3个空的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。必须在每1个用到greenlet C API的模块中调用2遍。
  • int PyGreenlet_Check(PyObject
    *p):1个宏定义,借使参数是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为设置成功,-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应该是叁个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循环中,如果想要协程病逝就擒获格林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地图