资讯专栏INFORMATION COLUMN

【协程原理】 - 为什么greenlet的状态无法被保存

verano / 1094人阅读

摘要:特别是最火的协程框架也无法保存状态,让人非常惋惜。但是因为栈的本身无法持久化,所以也就无法持久化。其难度在于,假设整个要持久化的调用栈全部都是内的,比如纯的。采取的是暴力地把整个栈区域拷贝到上的方式来保存其状态。

python主流的协程实现有五种:

cPython的generator

cPython的greenlet

cPython的fibers

stackless python

pypy

除了stackless python和pypy的实现版本之外,其余的协程都实现都无法保存状态。特别是最火的协程框架greenlet也无法保存状态,让人非常惋惜。使用stackless python在公司内部的项目里实现了在一台服务器上跑的任务,中断之后在另外一台服务器上继续执行,非常的awsome!
为什么greenlet的状态无法保存,而其前身stackless python就可以?我们可以先来看一下Java里的协程是如何实现的(http://www.slideshare.net/srirammalhar/communicating-state-machines)

这个是kilim的实现方式。大概的意思就是把栈的每一层frame的局部变量额外保存到协程对应的context里。因为局部变量不过是对heap上对象的引用,所以这些拷贝成本也很低。当前的执行位置使用label的方式标记,用goto可以恢复到之前的执行位置。

最后一个协程的状态要保存的话,就是一个纯Java的context对象做一下序列化就可以了,并不是非常困难。

这种实现协程的方式用stackless python的术语来说就是soft switching。而且kilim的实现也不是最优的,因为其记录的context信息在jvm的内部实现的栈上也有一份。只是jvm没有把这样的执行状态以api的形式暴露出来,使得开发者不得不额外耗费cpu指令来double booking的记录一份同样的信息到另外一个地方。stackless python和pypy就是把自己的内部的栈(形式和kilim的context的实现也差不多,就是frame的状态列表)实现成为可持久化的,所以他们的协程就是可以持久化的。

cPython的generator原理上也是soft switching。其generator内部保存的就是一个python栈的frame(目测3.3的yield from就是保存了一个frame的列表)。但是因为python栈的frame本身无法持久化,所以generator也就无法持久化。理论上来说通过bytecode级别的修改目标函数,可以和kilim一样在python vm之上利用其JMP_ABSOLUTE指令也是可以实现可持久化的协程的(https://docs.python.org/2/library/dis.html#bytecodes http://code.activestate.com/recipes/576944-the-goto-decorator/)。到目前为止,可能是因为stackless python等实现的存在,没有在python中修改bytecode的刚需,也就没有kilim这样的勇夫出现。

另外一类的协程实现是hard switching。所谓hard就是done in hard way的意思。其难度在于,soft switching假设整个要持久化的调用栈全部都是vm内的,比如纯python的。但是如果存在python => c扩展 => python这样的嵌套形式,那么调用栈上就会有c的栈,而不仅仅是python的栈。对于c的栈,我们是无法知道哪些区域是一个变量,而这个变量是一个普通的值,还是一个指向heap上某个位置的指针的。hard switching采取的是暴力地把整个栈区域拷贝到heap上的方式来保存其状态。对于只是switching来说,这样做是足够的了的。即便栈上放的是一个指针,要switch回来的时候把这个指针重新放回栈上就可以了。但是这种实现对于持久化来说是不够的,仅仅保存一个指针,而不保存指针指向的值的话,恢复出来的指针就会指向天晓得的某个区域了。

借一个图(http://www.slideshare.net/saghul/stack-switching-for-fun-and-profit):

hard switching有三份实现

stackless python 2.0 是纯hard switching的,3.0改成了soft switching和hard switching混合

greenlet 是Armin Rigo经过stackless作者启发,然后写的一个新的真正通用的hard switching的实现

pypy 里的 stacklet 是Armin Rigo新写的一个实现,据说更牛x

因为pypy的stacklet库是一个独立的实现,而且非常通用

fibers用其来实现了一个类似greenlet的协程框架(Armin Rigo大牛可能不屑于做这样简单的重复包装吧)

libgevent 用stacklet和pyuv实现了一个c版本的协程网络编程框架

不管stacklet怎么牛,其本质也是一个hard switching的实现,所以也无法实现状态的保存(本质就是一个调用栈内容的深拷贝)。所以python搞流程引擎,stackless还是不二之选。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/37399.html

相关文章

  • 【妙用协程】 - 可持久化协程用来实现工作流

    摘要:常规版本的的是不可以被持久化保存的。在流程被阻塞的时候比如需要审批老板不在把协程持久化成入库,等流程不再阻塞的时候把协程重新从数据库里拉起来继续执行。 常规版本的Python的generator是不可以被持久化保存的。但是stackless和pypy这两个修改版本的Python解释器可以。下面这段代码演示了如何把一个执行中的函数持久化保存,然后过段时间再把函数从上次执行到的地方原样拉起...

    wpw 评论0 收藏0
  • python---协程

    摘要:随着我们对于效率的追求不断提高,基于单线程来实现并发又成为一个新的课题,即只用一个主线程很明显可利用的只有一个情况下实现并发。作为的补充可以检测操作,在遇到操作的情况下才发生切换协程介绍协程是单线程下的并发,又称微线程,纤程。 引子 之前我们学习了线程、进程的概念,了解了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位。按道理来说我们已经算是把cpu的利用率提高很多了。...

    lolomaco 评论0 收藏0
  • 谈谈Python协程技术演进

    摘要:事件循环是异步编程的底层基石。对事件集合进行轮询,调用回调函数等一轮事件循环结束,循环往复。协程直接利用代码的执行位置来表示状态,而回调则是维护了一堆数据结构来处理状态。时代的协程技术主要是,另一个比较小众。 Coding Crush Python开发工程师 主要负责岂安科技业务风险情报系统redq。 引言 1.1. 存储器山 存储器山是 Randal Bryant 在《深入...

    zhiwei 评论0 收藏0
  • 【妙用协程】 - 可resume异常处理

    摘要:标准的异常处理是这样的这段代码会打印出而不会打印出,因为异常会中断当前流程,跳转到部分去继续执行。这种行为类似里的。如何实现的其实原理上很简单。的时候把当前协程的状态保存起来,如果决定要,就把协程的时刻的状态重新恢复然后从那个点继续执行。 标准的异常处理是这样的 try: print(hello) raise Exception() print(!!!) ex...

    Jeff 评论0 收藏0
  • 【generator101】 - 对比generator和greenlet

    摘要:比如里可以直接把执行权交给,而完全不知情。虽然不能和多线程相比,但是效果是类似的。对于多线程的代码,是任何一行代码都可能与其他线程并行。加上协程之间有共享状态的话,一定程度上会产生类似多线程的并发读写状态的。 前面讲generator是显式的协程的时候缺一个例子,现在补上 def parent_generator(): print(hello) yield from ...

    CoyPan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<