【发布时间】:2010-06-13 10:28:15
【问题描述】:
这只是说明我的问题的假设场景。假设它们之间共享两个线程和一个 TVar。在一个线程中有一个原子块读取 TVar 并需要 10 秒才能完成。在另一个线程中是一个原子块,它每秒修改一次 TVar。第一个原子块会完成吗?当然它会一直回到开头,因为日志永远处于不一致的状态?
【问题讨论】:
标签: haskell concurrency monads transactional-memory
这只是说明我的问题的假设场景。假设它们之间共享两个线程和一个 TVar。在一个线程中有一个原子块读取 TVar 并需要 10 秒才能完成。在另一个线程中是一个原子块,它每秒修改一次 TVar。第一个原子块会完成吗?当然它会一直回到开头,因为日志永远处于不一致的状态?
【问题讨论】:
标签: haskell concurrency monads transactional-memory
正如其他人所说:理论上没有进步的保证。在实践中也不能保证进步:
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 完成”。
显然,如果您认为计算仍然有效/相关,那么您可能想要
【讨论】:
unsafeIOToSTM 函数!
STM 可以防止死锁,但仍然容易受到饥饿的影响。在病态的情况下,1s 原子动作可能总是获取资源。
然而,这种情况发生的变化非常罕见——我相信我在实践中从未见过。
有关语义,请参阅Composable Memory Transactions,第 6.5 节“进度”。 Haskell 中的 STM 仅保证正在运行的事务将成功提交(即没有死锁),但在最坏的情况下,无限事务会阻塞其他事务。
【讨论】:
不,它会正常工作。两个线程如何交互取决于 重试逻辑。
例如,假设您有:
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保证的一部分。
【讨论】: