威尼斯人线上娱乐

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

14 4月 , 2019  

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

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

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

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

1   动机

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

八个”greenlet”,是2个更是原始的微线程的定义,不过尚未调度,恐怕叫做协程。那在您须求控制你的代码时很有用。你可以协调协会微线程的
调度器;也可以应用”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
是另一种缓解方案,未有锁和倒闭难点。你运转 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”
是3个很小的独自微线程。能够把它想像成二个堆栈帧,栈底是发端调用,而栈顶是当前greenlet的刹车地方。你使用greenlet创建一批那样的堆
栈,然后在他们中间跳转执行。跳转不是纯属的:1个greenlet必须挑选跳转到选择好的另1个greenlet,那会让前多少个挂起,而后一个回复。五个greenlet之间的跳转称为 切换(switch) 。

当您创建三个greenlet,它拿走八个初阶化过的空堆栈;当你首先次切换成它,他会运营钦定的函数,然后切换跳出greenlet。当最终栈底
函数甘休时,greenlet的堆栈又编制程序空的了,而greenlet也就死掉了。greenlet也会因为多少个未捕捉的不行死掉。

例如:

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

轻量级并发程序,使用Python中的greenlet包达成产出编程的入门教程。最终一行跳转到 test一() ,它打印1二,然后跳转到 test贰()
,打字与印刷5陆,然后跳转回 test壹() ,打字与印刷3四,然后 test一()
就甘休,gr1死掉。那时执行会回来原先的 gr壹.switch()
调用。注意,78是不会被打字与印刷的。

2.2   父greenlet

现行看望贰个greenlet死掉时进行点去何地。每一个greenlet拥有3个父greenlet。父greenlet在各种greenlet开首化时被成立(可是能够在别的时候改变)。父greenlet是当greenlet死掉时,继续原来的职位执行。那样,greenlet就被公司成1棵
树,一级的代码并不在用户创制的 greenlet
中运行,而号称主greenlet,相当于树根。

在地点的例证中,gr1和gr2都以把主greenlet作为父greenlet的。任何二个死掉,执行点都会重临主函数。

未捕获的相当会波及到父greenlet。借使上边包车型地铁 test贰()
包蕴2个打字与印刷错误(typo),他会生成3个 NameError
而干掉gr二,然后实施点会回到主函数。traceback会显示 test二() 而不是
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
属性执行,正是创设刻钦定的可怜。对于子类,能够定义3个 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” 和4二,前面边的例子的输出顺序相同。注意 test一()
和 test2() 的参数并不是在 greenlet
创造时内定的,而是在首先次切换成此处时传递的。

此间是准确的调用格局:

g.switch(obj=None or *args)

切换来实施点greenlet
g,发送给定的目的obj。在奇特情状下,假设g还尚无运行,就会让它运维;那种景观下,会传送参数过去,然后调用
g.run(*args) 。

垂死的greenlet

    假使三个greenlet的 run() 截至了,他会再次回到值到父greenlet。要是 run()
是老大终止的,卓殊会涉嫌到父greenlet(除非是 greenlet.格林letExit
相当,那种景色下10分会被捕捉并再次回到到父greenlet)。

除了上边的景况外,目的greenlet会接到到发送来的靶子作为 switch()
的重返值。纵然 switch()
并不会霎时回去,不过它依然会在今后某一点上回来,当其余greenlet切换回来时。当那发生时,执行点苏醒到
switch() 之后,而 switch() 重临刚才调用者发送来的目标。那表示
x=g.switch(y)
会发送对象y到g,然后等着贰个不知底是哪个人发来的对象,并在此间重返给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。

二.7   活动greenlet的废品收集

若是不再有对greenlet对象的引用时(包蕴别的greenlet的parent),还是尚未主意切换回greenlet。那种景况下会转变三个格林letExit
相当到greenlet。那是greenlet收到异步万分的绝无仅有情形。应该交由三个 try ..
finally
用于清理greenlet内的财富。那个成效并且同意greenlet中不过循环的编程风格。那样循环能够在最后三个引用消失时自动刹车。

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

greenlet不参与垃圾收集;greenlet帧的循环引用数据会被检测到。将引用传递到别的的循环greenlet会滋生内部存款和储蓄器走漏。

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

翻译自官方文书档案greenlet。

背景

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

多只来说,
3个“greenlet”任然是3个平昔不内部调度的关于微线程的比较原始的定义。换句话说,当您想要在您代码运转时形成规范控制,“greenlet”是1种很有用的章程。在greenlet基础之上,你可以定义自己的微线程调度策略。不管怎么样,greenlets也得以以一种高级控制流结构的不二等秘书籍用于他们自身。举个例子,我们能够再度生成迭代器。python自带的生成器与greenlet的生成器之间的差异是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表达的是,你不必要运用yield关键词,参见例子:test_generator.py)。

背景

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

一面来说,
多少个“greenlet”任然是二个平昔不中间调度的有关微线程的较为原始的概念。换句话说,当你想要在你代码运维时形成规范控制,“greenlet”是壹种很有用的方法。在greenlet基础之上,你能够定义自身的微线程调度策略。不管怎么着,greenlets也得以以一种高级控制流结构的点子用于他们自身。举个例子,大家得以另行生成迭代器。python自带的生成器与greenlet的生成器之间的区分是greenlet的生成器能够嵌套调用函数,并且嵌套函数也会yield值(补充表明的是,你不供给使用yield关键词,参见例子:test_generator.py)。

什么是greenlet

greenlet是从Stackless中分别的品种。greenlet也叫微线程、协程,它的调度是由程序显然决定的,所以进行流程是一定的、鲜明的。而线程的调度完全交由操作系统,执行顺序不能够预料。同时,协程之间切换的代价远比线程小。

greenlet是由此C扩大完毕的。

例子

笔者们来思考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成套工具是遵照事件驱动的。他们为每八个用户字符输入调用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
    greenlet壹有的的read_next_char()函数被调用,所以当接过到输入切换成上面greenlet,
    程序恢复生机到主循环(GUI)执行。
  • 当GUI调用event_keydown()的时候,程序切换来g_processor。那就象征程序跳出,无论它被挂起在那些greenlet什么地方。在那几个例子中,切换来read_next_char(),并且在event_keydown()中被按下的key作为switch()的结果回到给了read_next_char()。

亟待证实的是read_next_char()的挂起与回复都保留其调用堆栈。以便在prprocess_commands()中根据他来的地方苏醒到不相同的职位。那使得以壹种好的主宰流来控制造进度序逻辑成为大概。大家不用完全的重写process_commands(),将其更换为状态机。

例子

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

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

方今,要是你将顺序移植到GUI程序中,绝超越八分之四的GUI成套工具是基于事件驱动的。他们为每三个用户字符输入调用一个回调函数。(将“GUI”替换到“XML
expat
parser”,对你的话应该尤为熟练了)。在如此的气象中,执行上面包车型大巴函数read_next_char()是很劳苦的。那里是四个不匹配的函数:

def event_keydown(key):
    ??

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

您可能怀念用线程的章程来促成这些了。greenlets是另一种不须要关联锁与从不当机难点的可选的消除方案。你执行process_commands(),独立的greenlet。通过如下格局输入字符串。

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

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

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

gui.mainloop()

本条例子中,执行流程如下:

  • 当作为g_processor
    greenlet一局地的read_next_char()函数被调用,所以当接受到输入切换成上级greenlet,
    程序恢复生机到主循环(GUI)执行。
  • 当GUI调用event_keydown()的时候,程序切换来g_processor。这就意味着程序跳出,无论它被挂起在那些greenlet什么地方。在这几个事例中,切换成read_next_char(),并且在event_keydown()中被按下的key作为switch()的结果重返给了read_next_char()。

需求验证的是read_next_char()的挂起与回复都封存其调用堆栈。以便在prprocess_commands()中依照她来的地方恢复到差异的地点。那使得以壹种好的控制流来控制造进度序逻辑成为大概。大家无需完全的重写process_commands(),将其更换为状态机。

示例

有那样贰个类别,它根据用户在巅峰输入指令的两样而推行不一致的操作,假若输入是逐字符的。部分代码只怕是那样的:

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

近日大家想把那个顺序在GUI中落实。可是当先2/四GUI库都以事件驱动的,每当用户输入都会调用2个回调函数去处理。在那种情况下,假诺还想用上面的代码逻辑,恐怕是如此的:

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(切换)”。

当你创设3个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四,
    test1达成,并且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回切换来这些协程时,它调用三个破例的函数,这一个函数中得以调用其余函数,能够切换成其它协程等等。当最终栈底函数执行完后,协程的栈变为空,那时候,协程是死的(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贰,打印56,又切回到test壹打字与印刷34。然后test一甘休,gr1病逝。这时候执行回来了gr1.switch()调用。注意到,7捌并未被打字与印刷出。

父级greenlet

让大家看看当greenlet长逝的时候,程序执行到何地去了。每二个greenlet都有多个父级greenlet。最初的父级是创办greenlet的这些greenlet(父级greenlet是足以在任哪天候被更改)。父级greenlet是当一个greenlet寿终正寝的时候程序继续执行的地点。那种方法,程序组织成壹颗树。不在用户创立的greenlet中运转的顶层代码在隐式的主greenlet中运行,它是堆栈数的根。

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

并未有捕获的13分将抛出到父级greenlet中。举个例子,假如上边包车型客车test二()包括2个语法错误,它将生成2个杀死gr2的NameError错误,那个荒唐将直接跳转到主greenlet。错误堆栈将显示test二,而不会是test一。要求留意的是,switches不是调用,而是程序在相互的”stack
container(堆栈容器)”直接实施的跳转,“parent”定义了逻辑上位居当前greenlet之下的堆栈。

父级greenlet

让我们看看当greenlet身故的时候,程序执行到何地去了。每3个greenlet都有一个父级greenlet。最初的父级是开创greenlet的那个greenlet(父级greenlet是足以在别的时候被转移)。父级greenlet是当贰个greenlet过逝的时候程序继续执行的地点。那种艺术,程序组织成一颗树。不在用户成立的greenlet中运作的顶层代码在隐式的主greenlet中运作,它是堆栈数的根。

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

尚未捕获的丰盛将抛出到父级greenlet中。举个例子,如果上边的test二()包括一个语法错误,它将生成三个杀掉gr贰的NameError错误,那些错误将直接跳转到主greenlet。错误堆栈将展现test二,而不会是test一。须求留意的是,switches不是调用,而是程序在交互的”stack
container(堆栈容器)”直接实施的跳转,“parent”定义了逻辑上位居当前greenlet之下的库房。

父协程

种种协程都有一个父协程。协程在哪个体协会程中被创造,那么这一个协程正是父协程,当然后边可以转移。当有个别体协会程长逝后,会在父协程中继续执行。举个例子,在g第11中学创立了g二,那么g一正是g二的父协程,g2身故后,会在g第11中学继续执行。这么说的话,协程是树结构的。最上层的代码不是运作在用户定义的协程中,而是在贰个隐式的主协程中,它是协程树的根(root)。

在地方的例证中,gr壹和gr二的父协程都以主协程。不管哪1个凋谢,执行都会再次回到主协程。

可怜也会被传到父协程。比如说,test第22中学若包括了一个’typo’,就会掀起NameError格外,然后杀死gr贰,执行会平昔回到主协程。Traceback会展现test二而不是test1。注意,协程的切换不是调用,而是在平行的”栈容器”中传送执行。

实例化对象

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

  • greenlet(run=None,parent=None):创制四个新的greenlet对象(还尚无初阶运营)。run是三个可调用的函数,用来被调用。parent定义父级greenlet,暗许是时下greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:这几个奇异的非凡不会抛出到父级greenlet中,那足以用来杀死八个单一的greenlet。

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

实例化对象

greenlet.greenlet是2个体协会程类型,它帮忙一下操作:

  • greenlet(run=None,parent=None):创立一个新的greenlet对象(还一向不发轫运转)。run是一个可调用的函数,用来被调用。parent定义父级greenlet,私下认可是当前greenlet。
  • greenlet.getcurrent():获取当前greenlet(即,调用该函数的greenlet)
  • greenlet.格林letExit:那几个尤其的格外不会抛出到父级greenlet中,这能够用来杀死2个纯粹的greenlet。

greenlet类型可以被子类化。通过调用在greenlet创立的时候开首化的run属性来推行叁个greenlet。不过对于子类来说,定义3个run方法比提供3个run参数给构造器更有意义。

协程类

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

切换

当在3个greenlet中调用方法switch(),在greenlet之间的切换将发出,平常情形下,程序执行跳转到switch()被调用的greenlet中。大概当三个greenlet身故,程序执行将跳转到父级greenlet程序中,当发生切换的时候,2个目的或二个相当被发送到指标greenlet中。那是一种在四个greenlet中传递新闻的有益的措施。举个例子:

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

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

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

已与事先例子一样顺序执行,它将会打印“hello
world”与4二。多说一句,test一(),test贰()的参数不是在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中。或许当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”与4二。多说一句,test一(),test2()的参数不是在greenlet创立的时候给的,而是在首先次切换的时候给出。

那里给出了有关发送的数据的有目共睹的规则:

g.switch(*args, **kwargs):切换执行到greenlet
g,发送数据,作为2个非正规的例子,借使g未有实施,它将起来施行。

对于将死的greenlet。当run()完毕的时候,将会爆发对象给父级greenlet。假诺greenlet因为12分而平息,那一个足够将会抛出到父级greenlet中(greenlet.格林letExit例外,那几个那个被抓走了还要一向退出到父级greenlet中)。

而外下面例子描述的,平日目的greenlet(父级greenlet)接收此前调用switch()挂起,执行完成再次来到的重回值作为结果。事实上,就算对switch()的调用不会应声赶回结果,但是当其余1些greenlet切换回来的时候,在以往的有个别点将会重返结果。当切换爆发的时候,程序将在它前面挂起的地点恢复。switch()自身回来爆发的对象。那就意味着x=g.switch(y)将y给g,稍后将再次回到从某些不涉及的greenlet中回到的不关乎的对象给x变量。

晋升一下,任何准备切换来3个回老家的greenlet的将会走到离世greenlet的父级,也许父级的父级,以此类推(最后的父级是“main”
greenlet,它是尚未会死掉的)。

greenlet(run=None, parent=None)

制造一个新的协程对象。run是二个可调用对象,parent是父协程,默许是现阶段协程。

greenlets的主意与本性

  • g.switch(*args, **kwargs):切换程序到greenlet g中施行,参见下面。
  • g.run:当它初叶的时候,g的回调将会被执行,当g已经开端推行了,那特性格将不会设有了。
  • g.parent:父级greenlet。那是可编写制定属性,然则不可见写成了死循环。
  • g.gr_frame:最顶层的构造,或然等于None。
  • g.dead: bool值,当g死亡了,值为True。
  • bool(g):bool值,当重临结构是True,表示g还活蹦乱跳,假使是False,表示它去世了大概还没早先。
  • g.throw([typ, [val,
    [tb]]]):切换来g执行,不过及时抛出3个加以的不行。借使未有参数提供,私下认可十分是greenlet.格林letExit。同地点描述一样,寻常的不得了传递规则生效。调用该办法同上边代码是大致等于的:

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

    有少数不及的是,那段代码不能够用于greenlet.格林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。同地点描述1样,寻常的格外传递规则生效。调用该格局同上面代码是大约等于的:

      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线程结合起来。那种状态下,每一个线程包含1个单身的隐含一个子greenlets树的“main”
greenlet。混合或切换在分化线程中的greenlets是不容许的政工。

Greenlets与python的线程

格林lets将能够和python线程结合起来。这种处境下,每三个线程蕴含八个独自的隐含二个子greenlets树的“main”
greenlet。混合或切换在不一样线程中的greenlets是不可能的思想政治工作。

greenlet.GreenletExit

本条特殊的尤其不会传给父协程,常用来杀死协程。

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

greenlets的污物回收生命周期

如若对1个greenlet的全体涉嫌都早已失效(包含来自别的greenlets中的父级属性的涉及),那时候,没有其余一种方法切换回该greenlet中。这种情状下,格林letExit十分将会生出。那是二个greenlet接受异步执行的唯1办法。使用try:finally:语句块来清理被这么些greenlet使用的财富。那种属性支持一种编制程序风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的终极关联失效,那种循环将活动终止。

倘使greenlet要么回老家,要么根据留存有些地点的涉及苏醒。只要求捕获与忽略也许造成极端循环的格林letExit。

格林lets不插手垃圾回收。循环那多少个在greenlet框架中的数据时候,那个多元帅不会被检验到。循环的积存别的greenlets的引用将恐怕导致内部存款和储蓄器泄漏。

greenlets的垃圾堆回收生命周期

就算对一个greenlet的有所涉嫌都曾经失效(包涵来自别的greenlets中的父级属性的关系),那时候,未有别的一种艺术切换回该greenlet中。那种景色下,格林letExit非凡将会生出。那是1个greenlet接受异步执行的唯一办法。使用try:finally:语句块来清理被那么些greenlet使用的财富。这种属性扶助一种编程风格,greenletInfiniti循环等待数据同时实施。当对该greenlet的最终关联失效,那种循环将活动终止。

一旦greenlet要么回老家,要么依照留存有个别地方的关系恢复生机。只要求捕获与忽略或许造成极端循环的格林letExit。

Greenlets不参加垃圾回收。循环那么些在greenlet框架中的数据时候,这一个多少将不会被检查测试到。循环的仓库储存别的greenlets的引用将也许导致内部存款和储蓄器泄漏。

切换

有三种景况会发生协程之间的切换。1是有个别体协会程主动调用switch方法,那种景况下会切换成被调用的协程中。2是协程驾鹤归西,那时协程会切换来父协程。在切换时,二个目的或越发被传送到指标协程。那用来在协程之间传递新闻。如下边那个例子:

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模块中,有1部分新的方法:

  • greenlet.gettrace():重返先前已有的调用堆栈方法,只怕None。
  • greenlet.settrace(callback):设置2个新的调用堆栈方法,再次回到早先时期已有些艺术还是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 相关

格林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():3个宏定义,导入greenlet模块,先导化C
    API。必须在每四个用到greenlet C API的模块中调用一回。
  • int PyGreenlet_Check(PyObject
    *p):1个宏定义,尽管参数是Py格林let再次来到true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):3个宏定义,假若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不是一管用的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)内定的那1个,调用堆栈对象tb是可选的,并且能够为NULL。

关联

  • PyGreenlet_Import():一个宏定义,导入greenlet模块,开始化C
    API。必须在每二个用到greenlet C API的模块中调用贰遍。
  • int PyGreenlet_Check(PyObject
    *p):八个宏定义,假使参数是PyGreenlet再次来到true。
  • int PyGreenlet_STARTED(PyGreenlet
    *g):二个宏定义,假诺greenlet在开头了归来true。
  • int PyGreenlet_ACTIVE(PyGreenlet
    *g):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应该是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)

一旦协程处于活跃状态,则为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时,循环会自动刹车。

在最佳循环中,假设想要协程长逝就擒获格林letExit分外。要是想拥有三个新的引用就忽略GreenletExit。

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地图