如何将值从一个原子块传递到另一个原子块

时间:2014-11-26 17:19:26

标签: haskell ghc

在下面的代码中(Pl。参见myTransaction)我想从TVar n1原子地读取并更新另一个TVar n2,基于在一定延迟之后的读取值。问题是我无法将读取值从一个原子块传递到另一个原子块。

module SimpleSTM
where
import Control.Concurrent.STM
import Control.Concurrent
import System.IO.Unsafe
import System.Random 

type GNum = TVar Int
updateNum :: GNum -> Int -> STM ()
updateNum n v =  writeTVar n v 

myTransaction :: GNum -> GNum -> Int -> IO ()
myTransaction n1 n2 v = do
    atomically $ do 
        x <- readTVar n1
        let y =  (x + v )      -- Getting Error: Not an expression

    randomDelay

    atomically $ updateNum n2 y  -- Getting Error: Not in scope: 'y' 


randomDelay = do delay <- getStdRandom(randomR (1,3)) 
     threadDelay (delay * 1000000) 

main :: IO ()
main = do n1 <- newTVarIO 1
  n2 <- newTVarIO 1


  n1v <- readTVarIO n1
  n2v <- readTVarIO n2

  putStrLn ("n1 = " ++ (show n1v) ++ " n2= " ++ (show n2v))


  forkIO (myTransaction n1 n2 1)
  forkIO (myTransaction n2 n1 2)
  forkIO (myTransaction n2 n1 3)    

  n1v <- readTVarIO n1
  n2v <- readTVarIO n2

  putStrLn ("n1 = " ++ (show n1v) ++ " n2= " ++ (show n2v)) 

代码产生以下错误:

The last statement in a 'do' loop must be an expression
   let y = (x + v)
SimpleSTM1.hs:24:43: Not in scope: 'y'

请帮忙。提前谢谢。

1 个答案:

答案 0 :(得分:1)

你可以运行

myTransaction :: GNum -> GNum -> Int -> IO ()
myTransaction n1 n2 v = do
    y <- atomically $ do 
        x <- readTVar n1
        return (x + v)
    randomDelay
    atomically $ updateNum n2 y

但请注意,这会运行两个原子事务。即,读取和连续写入之间可能存在更新,导致整个myTransaction为非原子。


如果你真的想要原子延迟交易,你可以运行像

这样的东西
myTransaction :: GNum -> GNum -> Int -> IO ()
myTransaction n1 n2 v =
    atomically $ do 
        x <- readTVar n1
        unsafePerformIO randomDelay `seq` return ()
        updateNum n2 (x + v)

其中unsafePerformIO模拟纯计算需要一些时间来终止。请注意,unsafePerformIO通常是一个非常糟糕的想法,因为它真的不安全。但是,如果您使用它来模拟可以通过纯代码完成的操作,则可以使用它。

或者,unsafeIOtoSTM也有效,并带有与unsafePerformIO类似的警告。

但请注意,在“生产”代码中,您没有理由在事务中添加延迟。因此,我猜测你只是为了试验STM而想做这件事,也许是为了凭经验检查它的原子性保证。