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

有帮助吗?

解决方案

正如其他人所说:从理论上讲,并不能保证取得进展。在实践中也不能保证进展:

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. 将结果存储在原子块中,以便重试后仍然有效的结果只不过是廉价的查找。

其他提示

STM防止死锁,但仍是易受饥饿。有可能在对1S原子动作总是AQUIRE所述资源的情况下病理

不过,这种情况发生的变化是非常罕见的 - 我不相信我见过它在实践中

有关的语义,请参阅可组合存储器事务中,第6.5节“进步”。 STM在Haskell只保证一个运行的事务将成功地提交(即,没有死锁),但是在最坏情况下的无限交易将阻止他人。

没有,它会正常工作。究竟怎么了两个线程会相互作用取决于 重试逻辑。

例如,假设您有:

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计算。与外界沟通的唯一途径 世界是经由通过事务存储器传递的值。

和这意味着,如果通过事务存储器的“指挥棒传递”逻辑是 正确,程序将正常运行的确切数额的独立 时间的任何部分需要。这是STM的保证的一部分。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top