【发布时间】:2017-02-10 20:09:59
【问题描述】:
在 Haskell 中,我们有异步异常;我们可以使用throwTo 在另一个线程中引发任何异常:
throwTo :: Exception e => ThreadId -> e -> IO ()
throwTo在目标线程中引发任意异常(仅限 GHC)。
为了能够编写带有“获得锁后总是释放锁”等保证的代码,我们让mask 运行代码,其中异步异常只能在计算阻塞时接收:
mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b执行带有异步异常的 IO 计算屏蔽。也就是说,任何试图在当前线程中使用
throwTo引发异常的线程都将被阻塞,直到再次取消屏蔽异步异常。
还有一个更强大的uninterruptibleMask,其中在屏蔽计算期间根本不会引发异步异常:
uninterruptibleMask :: ((forall a. IO a -> IO a) -> IO b) -> IO b类似于
mask,但被屏蔽的计算不可中断
掩码用于实现更高级别的抽象,例如bracket:
bracket :: IO a -- computation to run first ("acquire resource") -> (a -> IO b) -- computation to run last ("release resource") -> (a -> IO c) -- computation to run in-between -> IO c -- returns the value from the in-between computation当你想获取一个资源,对它做一些工作,然后释放资源,使用
bracket是个好主意,因为bracket会安装必要的异常处理程序来释放资源在计算过程中引发异常的事件。如果引发异常,则bracket将重新引发异常(在执行释放之后)。
如果我理解正确的话,Python 有一种(不那么普遍的)异步异常形式,最显着的表现形式是KeyboardInterrupt:
当用户点击中断键时触发(通常是 Control-C 或 Delete)。在执行过程中,会定期检查中断。
文档对何时可能发生“检查中断”并不精确,但它似乎暗示KeyboardInterrupt 可能会在程序执行的任何 点引发。因此,Python 的异步异常似乎伴随着维护正确性的所有微妙困难。
例如,考虑这样的模式:
x = None
try:
x = acquire()
do_something(x) # (1)
finally:
if x is not None: # (2)
release(x)
如果在(1) 期间引发任何异常,那么我们确信finally 块的内容将被执行。但是如果KeyboardInterrupt 在(2) 期间会发生什么?
如果没有办法屏蔽它们,似乎根本不可能在存在 asyc 异常的情况下保证资源清理。有这方面的设施吗,还是我们依赖ostrich algorithm?
【问题讨论】:
-
如果你想这样做,我认为你几乎必须为 Ctrl-C 安装自己的信号处理程序。
标签: python haskell asynchronous exception-handling