gpt4 book ai didi

haskell - 这些线程是否永远被阻塞?

转载 作者:行者123 更新时间:2023-12-02 02:27:00 24 4
gpt4 key购买 nike

我目前正在阅读 Simon Marlow 的书“Haskell 中的并行和并发编程”,但我不明白这段代码:

waitAny :: [Async a] -> IO a
waitAny as = do
m <- newEmptyMVar
let forkwait a = forkIO $ do r <- try (wait a); putMVar m r
mapM_ forkwait as
wait (Async m)

这里我们调用 putMVar N 次,但我们只有 1 个等待操作。我是否正确理解 N-1 个线程将被阻止尝试执行 putMVar?这里发生了什么?

...或 super 简化:
test = do
m <- newEmptyMVar
forkIO $ putMVar m 1
forkIO $ putMVar m 2
a <- readMVar m
return a

为什么它可以毫无问题地工作?为什么我没有 异常(exception):线程在 MVar 操作中无限期阻塞 ?

最佳答案

Haskell 中关于并发的一些基本规则:

  • main线程退出,它立即杀死所有其他线程。如果你想让其他线程有机会清理,你必须明确地等待它们。
  • 辅助(非主)线程丢弃了一组特定的异常,因此当它们未被捕获时不会打印它们:

    The newly created thread has an exception handler that discards the exceptions BlockedIndefinitelyOnMVar, BlockedIndefinitelyOnSTM, and ThreadKilled, and passes all other exceptions to the uncaught exception handler.

    -- The Control.Concurrent.forkIO documentation

  • 当线程等待 MVar没有任何进展的希望,它会收到一个异常(exception)。但是由于上述问题,这在这个例子中是完全看不到的。请注意,由于垃圾收集器的特殊支持,这种方式只能捕获非常简单的一类死锁。不可能自动检测所有死锁。

  • 在您的第二个示例中,主线程(假设 main = test )在读取变量后立即退出,这没有时间让另一个线程(仍然阻塞在 putMVar 上的线程)使用react(上面的第 1 点)。所以首先添加一个 threadDelay在主线程结束时给另一个线程更多时间。这还不足以看出区别,因为辅助线程被 BlockedIndefinitelyOnMVar 杀死了。默默地(第2点)。在 putMVar 周围添加异常处理程序产生一个明确的输出。

    import Control.Concurrent
    import Control.Exception

    main :: IO ()
    main = do
    m <- newEmptyMVar :: IO (MVar Int)
    forkIO $ putMVar' m 1
    forkIO $ putMVar' m 2
    a <- readMVar m
    print a
    threadDelay 1000000 -- (1) Wait for other threads to clean up

    putMVar' :: MVar Int -> Int -> IO ()
    putMVar' r x =
    catch
    (putMVar r x)
    (\e ->
    putStrLn ("BLOCKED: " ++ show (x, e :: SomeException))) -- (2) Print something if the thread dies because of a deadlock

    {- Build this file with ghc -threaded ThisFile.hs
    Run it with ./ThisFile +RTS -N
    -}

    {- Output:

    1
    BLOCKED: (2,thread blocked indefinitely in an MVar operation)

    -}

    请注意 forkIO通常应该避免,因为它的级别太低了。从头开始实现同步需要付出很多努力。异步库提供了更方便的抽象。

    概括并从技术上回答您的问题:

    Here we call putMVar N times but we have only 1 wait operation. Do I understand it correct that N-1 threads will be blocked trying to execute putMVar? What is happening here?



    这是正确的想法。实际上,被阻塞的线程会得到一个异常,因为垃圾收集器可以看到 MVar无法从其他线程访问,但您不应该在生产中捕获并观察该异常,即使它是可能的,如上所示。确实, Control.Concurrent 的文档说了这么多:

    Note that this feature is intended for debugging, and should not be relied on for the correct operation of your program.

    -- The Control.Concurrent documentation



    Why does it work without problems? Why don't I have Exception: thread blocked indefinitely in an MVar operation?



    实际上会有这样的异常(exception),但是:
  • main线程退出太快,因为这也确实发生了;
  • 当非 main线程被 BlockedIndefinitelyOnMVar 杀死,他们不打印异常,您必须自己打印。
  • 关于haskell - 这些线程是否永远被阻塞?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62287347/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com