在 Simon Marlow 所著的《Haskell 中的并行和并发编程》中,展示了以下实现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 before after thing =
mask $ \restore -> do
a <- before
r <- restore (thing a) `onException` after a
_ <- after a
return r
并附有评论。以下是让我困惑的部分
[…] 包含阻塞操作是正常的;如果在阻塞
before
期间引发异常,则不会造成任何损害。但应该只执行一个阻塞操作。第二个阻塞操作引发的异常不会导致执行。[…]before
before
after
我不太理解该评论,所以我想对其进行一些澄清。
要清楚的是,我甚至不明白“没有造成任何伤害”这一部分:在前面几页(第 159 页)中,以下调用的失败(第二次)尝试takeMVar
,根据其内容执行操作,最后将该操作的结果放回MVar
via中,putMVar
如下所示,
problem :: MVar a -> (a -> IO a) -> IO ()
problem m f = mask $ \restore -> do
a <- takeMVar m
r <- restore (f a) `catch` \e -> do putMVar m a; throw e
putMVar m r
在看这个例子时,我“接受”一个事实,直到它返回时takeMVar
才会被篡改m
;事实上,这是从文本中摘取的:
直到返回时为止引发异常都是安全的
takeMVar
。
我觉得我明白接下来的内容(我认为)。
但回到 的实现bracket
,如果before
内部使用takeMVar
,并且在 之后发生异步异常takeMVar
(从而清空MVar
)会怎么样?这是否没有问题,因为我们使用了mask
+ restore
,也就是说,这样的异常会被延迟到 的参数(restore
即 )thing a
开始执行,此时必要的异常处理程序已经到位,在本例中是通过 `onException` after a
?
before
那么,如果再次调用阻塞操作,为了简单起见,再说一遍,会发生什么问题呢takeMVar
?问题是否是因为异常可能在阻塞时 takeMVar
发生,因此在异常未被屏蔽的时间窗口内,异常会冒泡出来bracket
,导致MVar
第一个参数takeMVar
不为空,但处于非原始状态,即 givenafter
还没有机会运行?
是这个吗?
此外,文档页面没有提及这一点,或者我不明白它是如何暗示的。