资讯专栏INFORMATION COLUMN

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

Jeff / 667人阅读

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

标准的异常处理是这样的

</>复制代码

  1. try:
  2. print("hello")
  3. raise Exception()
  4. print("!!!")
  5. except:
  6. print("world")
  7. print("???")

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

</>复制代码

  1. try:
  2. print("hello")
  3. print(Scheduler.interrupt())
  4. print("!!!")
  5. except ProcessInterrupt as pi:
  6. pi.resume("world")
  7. 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):

</>复制代码

  1. import greenlet
  2. import cPickle as pickle
  3. import traceback
  4. import threading
  5. import functools
  6. class ProcessInterrupt(Exception):
  7. def __init__(self, interruption_point, pi_args):
  8. self.interruption_point = interruption_point
  9. self.stacktrace = traceback.extract_stack()
  10. self.pi_args = pi_args
  11. def resume(self, resume_with=None):
  12. Scheduler.resume(self.interruption_point, resume_with)
  13. def __repr__(self):
  14. return ">>>ProcessInterrupt>>>%s" % repr(self.stacktrace)
  15. def __str__(self):
  16. return repr(self)
  17. def __unicode__(self):
  18. return repr(self)
  19. class Scheduler(object):
  20. current = threading.local()
  21. def __init__(self):
  22. if getattr(self.current, "instance", None):
  23. raise Exception("can not have two scheduler in one thread")
  24. self.scheduler_greenlet = greenlet.getcurrent()
  25. self.current.instance = self
  26. def __call__(self, action, action_args):
  27. next = action, action_args
  28. while next:
  29. action, action_args = next
  30. if "init" == action:
  31. next = action_args["init_greenlet"].switch()
  32. elif "interrupt" == action:
  33. interruption_point = pickle.dumps(action_args["switched_from"])
  34. should_resume, resume_with = False, None
  35. next = action_args["switched_from"].switch(
  36. should_resume, resume_with, interruption_point)
  37. elif "resume" == action:
  38. should_resume, resume_with, interruption_point = True, action_args["resume_with"], action_args[
  39. "interruption_point"]
  40. next = pickle.loads(action_args["interruption_point"]).switch(
  41. should_resume, resume_with, interruption_point)
  42. else:
  43. raise NotImplementedError("unknown action: %s" % action)
  44. @classmethod
  45. def start(cls, init_func, *args, **kwargs):
  46. scheduler = Scheduler()
  47. init_greenlet = greenlet.greenlet(functools.partial(init_func, *args, **kwargs))
  48. scheduler("init", {
  49. "init_greenlet": init_greenlet,
  50. })
  51. @classmethod
  52. def interrupt(cls, pi_args=None):
  53. should_resume, resume_with, interruption_point = cls.switch_to_scheduler("interrupt", {
  54. "switched_from": greenlet.getcurrent()
  55. })
  56. if should_resume:
  57. return resume_with
  58. else:
  59. pi = ProcessInterrupt(interruption_point, pi_args)
  60. raise pi
  61. @classmethod
  62. def resume(cls, interruption_point, resume_with=None):
  63. cls.switch_to_scheduler("resume", {
  64. "interruption_point": interruption_point,
  65. "resume_with": resume_with
  66. })
  67. @classmethod
  68. def switch_to_scheduler(cls, *args, **kwargs):
  69. return cls.current.instance.scheduler_greenlet.switch(*args, **kwargs)
  70. if "__main__" == __name__:
  71. def init():
  72. try:
  73. print("hello")
  74. print(Scheduler.interrupt())
  75. print("!!!")
  76. except ProcessInterrupt as pi:
  77. pi.resume("world")
  78. print("???")
  79. try:
  80. print("hello")
  81. raise Exception()
  82. print("!!!")
  83. except:
  84. print("world")
  85. print("???")
  86. 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元查看
<