Haskell中mask/restore作用验证:如何修改测试代码显化差异
Haskell中bracket的mask机制差异测试方法
你当前的测试没体现出mask的作用,是因为mask的核心是阻止异步异常中断被包裹的代码段——标准库的bracket用mask保护了资源获取(acquire)和释放(release)的过程,而你的myBracket没有这个保护。要显化差异,需要在acquire或release的执行过程中发送异步异常,而非等work阶段或流程后期发异常。
测试前置代码
先定义通用的异常、acquire、release和work函数:
import Control.Concurrent import Control.Exception import Control.Monad data MyException = MyException deriving (Show, Exception) -- 带有延迟的资源获取,方便在执行过程中发送异常 acquire :: IO () acquire = do putStrLn "Starting acquire" threadDelay 1000000 -- 延迟1秒 putStrLn "Acquire done" -- 带有延迟的资源释放 release :: () -> IO () release _ = do putStrLn "Starting release" threadDelay 1000000 putStrLn "Release done" work :: () -> IO () work _ = putStrLn "Working..."
场景1:在acquire执行期间发送异步异常
测试标准库bracket
main :: IO () main = do tid <- forkIO $ bracket acquire release work threadDelay 500000 -- 等待500ms,此时acquire仍在执行 throwTo tid MyException threadDelay 2000000 -- 等待流程结束
输出结果:
Starting acquire Acquire done Starting release Release done
原因:mask保护了acquire的执行,即使收到异步异常,也会先完成资源获取,再执行release释放资源,最后响应异常。
测试自定义myBracket
main :: IO () main = do tid <- forkIO $ myBracket acquire release work threadDelay 500000 throwTo tid MyException threadDelay 2000000
输出结果:
Starting acquire
原因:没有mask保护,acquire执行到一半时被异步异常中断,后续的release完全不会执行,资源泄露。
场景2:在release执行期间发送异步异常
测试标准库bracket
main :: IO () main = do tid <- forkIO $ bracket acquire release work threadDelay 1500000 -- 等待acquire和work完成,release开始执行 throwTo tid MyException threadDelay 2000000
输出结果:
Starting acquire Acquire done Working... Starting release Release done
原因:mask保护了release的执行,异步异常不会中断资源释放流程。
测试自定义myBracket
main :: IO () main = do tid <- forkIO $ myBracket acquire release work threadDelay 1500000 throwTo tid MyException threadDelay 2000000
输出结果:
Starting acquire Acquire done Working... Starting release
原因:release执行到一半被异步异常中断,资源未完全释放。
核心差异总结
标准库bracket的mask将整个资源生命周期包裹,仅在restore (thing a)(即业务逻辑work)阶段允许异步异常中断;而你的myBracket没有这个保护,资源获取和释放的过程都可能被异步异常打断,这会导致资源泄露或释放不彻底。
内容的提问来源于stack exchange,提问作者WHITECOLOR
相关产品推荐
相关产品推荐

