【问题标题】:STM monad problemSTM单子问题
【发布时间】:2010-06-13 10:28:15
【问题描述】:

这只是说明我的问题的假设场景。假设它们之间共享两个线程和一个 TVar。在一个线程中有一个原子块读取 TVar 并需要 10 秒才能完成。在另一个线程中是一个原子块,它每秒修改一次 TVar。第一个原子块会完成吗?当然它会一直回到开头,因为日志永远处于不一致的状态?

【问题讨论】:

    标签: haskell concurrency monads transactional-memory


    【解决方案1】:

    正如其他人所说:理论上没有进步的保证。在实践中也不能保证进步:

    import Control.Monad -- not needed, but cleans some things up
    import Control.Monad.STM
    import Control.Concurrent.STM
    import Control.Concurrent
    import GHC.Conc
    import System.IO
    
    main = do
        tv <- newTVarIO 0
        forkIO (f tv)
        g tv
    
    f :: TVar Int -> IO ()
    f tv = forever $ do
        atomically $ do
                n <- readTVar tv
                writeTVar tv (n + 1)
                unsafeIOToSTM (threadDelay 100000)
        putStr "."
        hFlush stdout
    
    g :: TVar Int -> IO ()
    g tv = forever $ do
        atomically $ do
                n <- readTVar tv
                writeTVar tv (n + 1)
                unsafeIOToSTM (threadDelay 1000000)
        putStrLn "Done with long STM"
    

    在我的测试中,上述内容从未说过“用长 STM 完成”。

    显然,如果您认为计算仍然有效/相关,那么您可能想要

    1. 离开原子块,执行昂贵的计算,进入原子块/确认假设有效/并更新值。有潜在危险,但并不比大多数锁定策略更危险。
    2. 将结果存储在原子块中,因此仍然有效的结果只不过是重试后的廉价查找。

    【讨论】:

    • 很好的例子。我想用这样的东西进行测试,但我不知道 unsafeIOToSTM 函数!
    【解决方案2】:

    STM 可以防止死锁,但仍然容易受到饥饿的影响。在病态的情况下,1s 原子动作可能总是获取资源。

    然而,这种情况发生的变化非常罕见——我相信我在实践中从未见过。

    有关语义,请参阅Composable Memory Transactions,第 6.5 节“进度”。 Haskell 中的 STM 仅保证正在运行的事务将成功提交(即没有死锁),但在最坏的情况下,无限事务会阻塞其他事务。

    【讨论】:

    • STM 不一定非阻塞。
    【解决方案3】:

    不,它会正常工作。两个线程如何交互取决于 重试逻辑。

    例如,假设您有:

    ten tv = do
      n <- readTVar tv
      when (n < 7) retry
      writeTVar tv 0
      -- do something that takes about 10 seconds
    
    one tv = do
      modifyTVar tv (+1)
      -- do something that takes about 1 second
    

    所以“ten”线程将处于重试状态,直到 TVar 到达 值 7,那么它将继续。

    请注意,您无法直接控制这些计算需要多长时间 在 STM 单子内部。那将是一个副作用,而副作用不是 允许在 STM 计算中。与外界沟通的唯一途径 world 是通过事务内存传递的值。

    这意味着如果通过事务内存的“接力棒”逻辑是 正确,程序将独立于确切数量正常工作 时间的任何部分。这是STM保证的一部分。

    【讨论】:

    • 我真的在考虑最坏的情况。让我们暂时忘记重试,只考虑两个线程,并考虑 STM '10' 的执行。它读取 TVar,并将该值提交到日志。同时,另一个线程总是在执行“十”期间更改该 TVar。所以,在 'ten' 执行结束时,真实 TVar 中的值与 'ten' 中最初读取的值不一样,迫使 'ten' 重新初始化日志,每次都重新执行。
    • 正如 Yitz 所说,这取决于。是的,有可能构建一个事务永远无法完成的情况。或者更正式地说,STM 不保证进度。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    相关资源
    最近更新 更多