资讯专栏INFORMATION COLUMN

【妙用协程】 - 可resume的异常处理

Jeff / 570人阅读

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

标准的异常处理是这样的

try:
    print("hello")
    raise Exception()
    print("!!!")
except:
    print("world")
    print("???")

这段代码会打印出???而不会打印出!!!,因为异常会中断当前流程,跳转到except部分去继续执行。但是有的时候我们希望的这样的行为:

try:
    print("hello")
    print(Scheduler.interrupt())
    print("!!!")
except ProcessInterrupt as pi:
    pi.resume("world")
    print("???")

这段代码打印出!!!而不是???,因为resume的时候把执行重新跳转回interrupt的地方了。这种行为类似vba里的on error resume next(https://msdn.microsoft.com/en-us/library/5hsw66as.aspx)。

如何实现的?其实原理上很简单。interrupt的时候把当前协程的状态保存起来(pickle.dumps),如果决定要resume,就把协程interrupt的时刻的状态重新恢复(pickle.loads)然后从那个点继续执行。

完整的代码(需要pypy或者stackless python):

import greenlet
import cPickle as pickle
import traceback
import threading
import functools


class ProcessInterrupt(Exception):
    def __init__(self, interruption_point, pi_args):
        self.interruption_point = interruption_point
        self.stacktrace = traceback.extract_stack()
        self.pi_args = pi_args

    def resume(self, resume_with=None):
        Scheduler.resume(self.interruption_point, resume_with)

    def __repr__(self):
        return ">>>ProcessInterrupt>>>%s" % repr(self.stacktrace)

    def __str__(self):
        return repr(self)

    def __unicode__(self):
        return repr(self)


class Scheduler(object):
    current = threading.local()

    def __init__(self):

        if getattr(self.current, "instance", None):
            raise Exception("can not have two scheduler in one thread")
        self.scheduler_greenlet = greenlet.getcurrent()
        self.current.instance = self

    def __call__(self, action, action_args):
        next = action, action_args
        while next:
            action, action_args = next
            if "init" == action:
                next = action_args["init_greenlet"].switch()
            elif "interrupt" == action:
                interruption_point = pickle.dumps(action_args["switched_from"])
                should_resume, resume_with = False, None
                next = action_args["switched_from"].switch(
                    should_resume, resume_with, interruption_point)
            elif "resume" == action:
                should_resume, resume_with, interruption_point = True, action_args["resume_with"], action_args[
                    "interruption_point"]
                next = pickle.loads(action_args["interruption_point"]).switch(
                    should_resume, resume_with, interruption_point)
            else:
                raise NotImplementedError("unknown action: %s" % action)

    @classmethod
    def start(cls, init_func, *args, **kwargs):
        scheduler = Scheduler()
        init_greenlet = greenlet.greenlet(functools.partial(init_func, *args, **kwargs))
        scheduler("init", {
            "init_greenlet": init_greenlet,
        })

    @classmethod
    def interrupt(cls, pi_args=None):
        should_resume, resume_with, interruption_point = cls.switch_to_scheduler("interrupt", {
            "switched_from": greenlet.getcurrent()
        })
        if should_resume:
            return resume_with
        else:
            pi = ProcessInterrupt(interruption_point, pi_args)
            raise pi

    @classmethod
    def resume(cls, interruption_point, resume_with=None):
        cls.switch_to_scheduler("resume", {
            "interruption_point": interruption_point,
            "resume_with": resume_with
        })

    @classmethod
    def switch_to_scheduler(cls, *args, **kwargs):
        return cls.current.instance.scheduler_greenlet.switch(*args, **kwargs)


if "__main__" == __name__:
    def init():
        try:
            print("hello")
            print(Scheduler.interrupt())
            print("!!!")
        except ProcessInterrupt as pi:
            pi.resume("world")
            print("???")

        try:
            print("hello")
            raise Exception()
            print("!!!")
        except:
            print("world")
            print("???")

    Scheduler.start(init)

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

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

相关文章

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

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

    wpw 评论0 收藏0
  • Lua5.3学习笔记

    摘要:对于正常结束,将返回,并接上协程主函数的返回值。当错误发生时,将返回与错误消息。通过调用使协程暂停执行,让出执行权。通用形式的通过一个叫作迭代器的函数工作。 Lua 是一门强大、轻量的嵌入式脚本语言,可供任何需要的程序使用。Lua 没有 main 程序的概念: 它只能 嵌入 一个宿主程序中工作.宿主程序可以调用函数执行一小段 Lua 代码,可以读写 Lua 变量,可以注册 C 函数让 ...

    AWang 评论0 收藏0
  • [译]PEP 342--增强型生成器:协程

    摘要:新语法表达式语句可以被用在赋值表达式的右侧在这种情况下,它就是表达式。表达式必须始终用括号括起来,除非它是作为顶级表达式而出现在赋值表达式的右侧。 showImg(https://segmentfault.com/img/bVbnQsb?w=4344&h=2418);PEP原文 : https://www.python.org/dev/pe... PEP标题: Coroutines v...

    Cheng_Gang 评论0 收藏0

发表评论

0条评论

Jeff

|高级讲师

TA的文章

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