在下面的代码中(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'
请帮忙。提前谢谢。
答案 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而想做这件事,也许是为了凭经验检查它的原子性保证。