当前位置: 首页 > 工具软件 > greenlet > 使用案例 >

Python之greenlet学习

宋涵衍
2023-12-01

greenlet官方文档:点击打开链接

以下内容主要是理解官方文档而来.

greenlet

   greenlet是python的并行处理的一个库。 python 有一个非常有名的库叫做 stackless ,用来做并发处理, 主要是弄了个叫做tasklet的微线程的东西, 而greenlet 跟stackless的最大区别是greenlet需要你自己来处理线程切换, 就是说,你需要自己指定现在执行哪个greenlet再执行哪个greenlet。

使用

 Introduction

  一个“greenlet”是一个独立的小微线程。可以把它想象成一个堆栈,栈底是初始功能,而栈顶是当前greenlet的暂停位置。你可以通过创建多个这样的堆栈,然后在它们之间的跳跃执行。跳转不是绝对的:一个greenlet必须选择跳转到一个已经选择好的greenlet,这将导致前者挂起,后者恢复。 greenlets之间跳转被称为切换(switch)。
  当你创建一个greenlet,它得到一个初始的空栈;当你第一次切换到它,它开始运行指定的函数,比如可以调用其他函数,切换出greenlet等。当最终栈底函数结束时,greenlet的堆栈再次变成空,greenlet也就dead了。 greenlets也会因为一个未捕获的异常而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()
输出:
12
56
34

  最后一行跳转到 test1() ,它打印12,然后跳转到 test2() ,打印56,然后跳转回 test1() ,打印34,然后 test1() 就结束,gr1 dead。这时执行会回到原来的 gr1.switch() 。

 Parents

  让我们看看当一个greenlet dead时执行到了哪里。每个greenlet拥有一个“父”greenlet。父greenlet在每个greenlet初始化时创建(这可以在任何时候被改变)。父greenlet是当greenlet dead时,在原来的位置继续执行。通过这种方式,greenlet以树的形式组织。层次最高的代码不会在用户创建的freenlet中运行,称为主greenlet,也就是树根。
  在上述的例子中,gr1和gr2都把主greenlet作为父greenlet。当其中任一greenlet dead时,执行点都会回到主greenlet。
  未捕获的异常也会影响到父greenlet。例如,如果上述test2()包含一个异常,它会杀死gr2,然后执行点回到主函数。回溯会显示test2()而不是test1()。请记住,切换不是调用,但是执行点可以在并行的栈容器间并行交换,而父greenlet定义了栈最初从哪里来。

 Instantiation

  greenlet.greenlet是一个greenlet类型,它支持以下的操作:
   greenlet(run=None, parent=None)
    创建一个新的greenlet对象(不运行它)。run是可调用的对象,parent是父greenlet,它默认为当前greenlet。
   greenlet.getcurrent()
    返回当前的greenlet
   greenlet.GreenletExit
    这个特殊的异常不会传播到父greenlet;它可以被用来杀死一个greenlet。
   greenlet 类型可以被继承。一个greenlet通过调用其 run 属性执行,就是创建时指定的那个。对于子类,可以定义一个 run() 方法,而不必严格遵守在构造器中给出 run 参数。

 Switching

  greenlets之间切换通过调用greenlet的switch()方法,在这种情况下,执行点跳转到调用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

这里是精确的调用方式:

g.switch(*args, **kwargs)
切换执行点到greenlet g,传递给定的参数。如果g还没有启动,那么它会现在就开始运行。

 Dying greenlet

  如果一个greenlet的 run() 结束了,它会返回到父greenlet。如果 run() 是异常终止的,异常会影响到父greenlet(除非是 greenlet.GreenletExit 异常,这种情况下异常会被捕捉并返回到父greenlet)。
  除了上面的情况外,目标greenlet会接收到发送来的对象作为 switch() 的返回值。虽然 switch() 并不会立即返回,但是它仍然会在未来某一点上返回,当其他greenlet切换回来时。当这发生时,执行点恢复到 switch() 之后,而 switch() 返回刚才调用者发送来的对象。这意味着 x=g.switch(y) 会发送对象y到g,然后等着一个不知道是谁发来的对象,并在这里返回给x。
  注意,任何尝试切换到死掉的greenlet的行为都会切换到死掉greenlet的父greenlet,或者父的父,等等。最终的父就是 main greenlet,永远不会死掉的。

 Methods and attributes of greenlets

  g.switch(*args, **kwargs)
   把执行点切换到greenlet g.
  g.run
    调用可执行的g,并启动。在g启动后,这个属性就不再存在了。
  g.parent
   父greenlet,可以更改,但是不允许创建循环的关系.
  g.gr_frame
   当前顶级帧或者为空.
  g.dead
   如果g dead了则返回True
  bool(g)
           如果g 是活动的则返回True,如果g是dead或者还未启动则返回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()

greenlet与线程

  greenlet可以与Python线程一起使用;在这种情况下,每个线程包含一个独立的 main greenlet,并拥有自己的greenlet树。不同线程之间不可以互相切换greenlet。

 类似资料: