了解并发代码中的BlockedIndefinitelyOnMVar

jbe*_*man 5 concurrency haskell ghc

我在ghc-users邮件列表上问了这个问题并得到了一些有用的回复,但仍然不明白这段代码中发生了什么.

本质上我试图理解如何捕获异常BlockedIndefinitelyOnMVar来恢复可能尚未返回的锁,并通常理解此异常.

以下是一些单线程代码:

-- This raises the exception only once and the lock is successfully restored:
main1 = do
    lock <- newMVar ()
    lockPrint "good1" lock
    takeMVar lock 
    putStrLn "main: took lock but didn't return it!"
    -- exception is raised and lock is restored here:
    lockPrint "good2" lock
    -- no exception raised:
    lockPrint "good3" lock
    readMVar lock
    putStrLn "great success"

lockPrint :: String -> MVar () -> IO ()
lockPrint name v =  takePrint `finally` put 
    where put = putMVar v () >> putStrLn (name++": replaced lock")
          takePrint = do
               e <- try $ takeMVar v :: IO (Either BlockedIndefinitelyOnMVar ())
               let printExc = putStrLn . ((name++": ")++) . show
                   printSuccess = const $ putStrLn (name++": success")
               either printExc printSuccess e
Run Code Online (Sandbox Code Playgroud)

以下是展示我不理解的行为的主要版本.特别是我不太确定为什么在main中引发异常,尽管我看到线程并没有像我想象的那样被安排.

main0 = do
    lock <- newMVar ()
    forkIO $ lockPrint "good1" lock

    threadDelay 100000
    takeMVar lock
    putStrLn "main: took lock but didn't return it!"

    -- raises blocked indefinitely exception
    forkIO $ lockPrint "good2" lock

    -- this should raise no exception if we were successful above:
    putStrLn "main: long pause..."
    threadDelay 2000000
    readMVar lock
    putStrLn "great success"
Run Code Online (Sandbox Code Playgroud)

对不起,我很难想出一个更简单的例子.以上编译如下:ghc --make -threaded -fforce-recomp experiments.hs


编辑:爱德华Z.杨今天在这里写了一篇非常清晰的博客文章.结果是,这个例外不能真正依赖于做任何花哨的事情.

ham*_*mar 3

BlockedIndefinitelyOnMVar首先尝试处理听起来并不是一个好主意。它更容易使用来确保始终返回withMVar的内容。MVar这样,您就不应该首先遇到此异常,除非您遇到死锁(这应该被视为错误并且应该在代码中修复)。