STM monad problem

谁说我不能喝 提交于 2019-11-29 06:00:47

As others have said: in theory there is no guarantee of progress. In practice there is also no guarantee of progress:

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"

The above never says "Done with long STM" in my tests.

Obviously if you think the computation is still going to be valid/pertinent then you would want to either

  1. Leave the atomic block, perform expensive computation, enter the atomic block / confirm assumptions are valid / and update the value. Potentially dangerous, but no more so than most locking strategies.
  2. Memoize the results in the atomic block so the still valid result will be no more than a cheap lookup after a retry.

STM prevents deadlock, but is still vulnerable to starvation. It is possible in a pathological case for the 1s atomic action to always aquire the resource.

However, the changes of this happening are very rare -- I don't believe I've ever seen it in practice.

For the semantics, see Composable Memory Transactions, section 6.5 "Progress". STM in Haskell guarantees only that a running transaction will successfully commit (i.e. no deadlock), but in the worst case an infinite transaction will block others.

No, it would work fine. Exactly how the two threads would interact depends on the retry logic.

For example, let's say you have:

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

So the "ten" thread will be in retry state until the TVar reaches the value 7, then it will proceed.

Note that you can't directly control how long these computations will take inside the STM monad. That would be a side-effect, and side-effects are not allowed in STM calculations. The only way to communicate with the outside world is via values passed through transactional memory.

And that means that if the "baton-passing" logic through transactional memory is correct, the program will work correctly independently of the exact amount of time any part of it takes. That's part of the guarantee of STM.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!