常规版本的Python的generator是不可以被持久化保存的。但是stackless和pypy这两个修改版本的Python解释器可以。下面这段代码演示了如何把一个执行中的函数持久化保存,然后过段时间再把函数从上次执行到的地方原样拉起。从效果上来说,有点类似于Vmware虚拟机的snapshot的功能:
import cPickle as pickle
def generator_can_be_used_as_workflow():
yield \'do something\'
is_approved = yield \'ask boss for permission\'
if is_approved:
yield \'do another after approved\'
else:
yield \'do another after rejected\'
workflow = generator_as_workflow_engine()
print(workflow.next())
print(workflow.next())
# boss is not available now
persisted_workflow = pickle.dumps(workflow)
print(\'persisted workflow is like this: [%s ...]\' % persisted_workflow.replace(\'\\n\', \' \')[:150])
# several hours later, boss come back
workflow = pickle.loads(persisted_workflow)
print(workflow.send(True)) # I approve
这段代码的输出是
do something
ask boss for permission
persisted workflow is like this: [cstackless._wrap generator p1 (tRp2 (cstackless._wrap frame p3 (cstackless._wrap code p4 (I0 I1 I1 I99 S\'d\\x01\\x00V\\x01d\\x02\\x00V}\\x00\\x00|\\x00\\x00r\\x ...]
do another after approved
利用这个原理,我们可以把一个需要运行很长时间的流程用协程的方式来实现。在流程被阻塞的时候(比如需要审批老板不在)把协程持久化成string入库,等流程不再阻塞的时候把协程重新从数据库里拉起来继续执行。优点自然是轻量简单随意强大,缺点也是随意强大导致流程状态不可被外部直接解读和操作,也无法实现运行中的流程实例的代码升级。所以,这种工作流用在电子政务,或者办公自动化等强人机交互的领域(需要极高的灵活性)是不合适的。在运维的发布变更这样的场合下,主要是调度系统与系统,机器与机器这样比较固定且相对短暂的流程还是比较方便的。
ps:webwork使用的RIFE,是我所知最早的使用协程的流程引擎
下面是一段演示用pypy的greenlet,控制流程前进和回退的例子:
import greenlet
if \'__main__\' == __name__:
root = greenlet.getcurrent()
def g():
print(\'enter g\')
mystack = \'special-g-stack\'
greenlet.getcurrent().parent.switch()
print(\'leave g\')
def f():
greenlet_g = greenlet.greenlet(g)
greenlet_g.switch()
root.switch()
print(\'a\')
greenlet_g.switch()
root.switch()
print(\'b\')
root.switch()
print(\'c\')
f_greenlet = greenlet.greenlet(f)
f_greenlet.switch()
f_greenlet_v1 = pickle.dumps(f_greenlet)
print(\'is greenlet g also pickled? %s\' % (\'special-g-stack\' in f_greenlet_v1))
f_greenlet = pickle.loads(f_greenlet_v1)
f_greenlet.switch()
f_greenlet_v2 = pickle.dumps(f_greenlet)
f_greenlet.switch()
f_greenlet_v3 = pickle.dumps(f_greenlet)
pickle.loads(f_greenlet_v1).switch()
pickle.loads(f_greenlet_v3).switch()
输出是:
enter g
is greenlet g also pickled? True
a
leave g
b
a
leave g
c
通过loads不同版本的流程状态,可以在各个阶段里来回切换。