我在 STM 上被屏蔽的确切原因是什么?
What is the precise reason I got blocked on STM?
我有以下 Haskell 代码,它应该实现一些基于 STM 的队列:
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Exception
import Control.Monad (forever)
import Hevents.Eff
import System.IO
withStore :: (FileStorage -> IO a) -> IO a
withStore = bracket (openFileStorage "test.store") closeFileStorage
data Op = Op String (TMVar Int)
storerun :: TBQueue Op -> IO ()
storerun q = do
h <- openFile "store.test" ReadWriteMode
hSetBuffering h NoBuffering
forever $ do
Op s v <- atomically $ readTBQueue q
hPutStrLn h s
atomically $ putTMVar v (length s)
main :: IO ()
main = do
q <- newTBQueueIO 100
_ <- async $ storerun q
storeInput q
where
storeInput q = forever $ do
putStrLn "pushing"
l <- getLine
v <- newEmptyTMVarIO
r <- atomically $ do
writeTBQueue q (Op l v)
takeTMVar v
putStrLn $ "got " ++ show r
当 运行 此代码引发 BlockedIndefinitelyOnSTM
异常。如果我将 storeInput
函数更改为以下内容:
storeInput q = forever $ do
putStrLn "pushing"
l <- getLine
v <- atomically $ do
v <- newEmptyTMVar
writeTBQueue q (Op l v)
return v
r <- atomically $ takeTMVar v
putStrLn $ "got " ++ show r
程序运行良好。
我对导致此异常的原因的理解是,STM t运行saction 中涉及的变量以某种方式被垃圾收集,仅在 retry
ing 和因此陷入僵局,因为 t运行sactional 变量的内容永远不会改变。
在我的代码中,Op
结构中的 v
变量是在一个线程中创建的,使用 t运行sactional 队列传递给另一个线程,然后由另一个线程使用,而且似乎没有理由在任何线程中对其进行垃圾收集。
因此我不清楚为什么这段代码会失败。
事务是原子的。问题出在这里:
r <- atomically $ do
writeTBQueue q (Op l v) -- (1)
takeTMVar v -- (2)
这将阻塞,除非另一个线程在 (1) 和 (2) 之间执行 putTMVar
。原子性阻止了这种情况。
在一笔交易中,您不能 "send information" 到另一笔交易,并期望从那笔交易中得到 "reply"。这将要求前一个事务(逻辑上)在后一个事务之前执行,反之亦然,这是不可能的。
我有以下 Haskell 代码,它应该实现一些基于 STM 的队列:
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Exception
import Control.Monad (forever)
import Hevents.Eff
import System.IO
withStore :: (FileStorage -> IO a) -> IO a
withStore = bracket (openFileStorage "test.store") closeFileStorage
data Op = Op String (TMVar Int)
storerun :: TBQueue Op -> IO ()
storerun q = do
h <- openFile "store.test" ReadWriteMode
hSetBuffering h NoBuffering
forever $ do
Op s v <- atomically $ readTBQueue q
hPutStrLn h s
atomically $ putTMVar v (length s)
main :: IO ()
main = do
q <- newTBQueueIO 100
_ <- async $ storerun q
storeInput q
where
storeInput q = forever $ do
putStrLn "pushing"
l <- getLine
v <- newEmptyTMVarIO
r <- atomically $ do
writeTBQueue q (Op l v)
takeTMVar v
putStrLn $ "got " ++ show r
当 运行 此代码引发 BlockedIndefinitelyOnSTM
异常。如果我将 storeInput
函数更改为以下内容:
storeInput q = forever $ do
putStrLn "pushing"
l <- getLine
v <- atomically $ do
v <- newEmptyTMVar
writeTBQueue q (Op l v)
return v
r <- atomically $ takeTMVar v
putStrLn $ "got " ++ show r
程序运行良好。
我对导致此异常的原因的理解是,STM t运行saction 中涉及的变量以某种方式被垃圾收集,仅在 retry
ing 和因此陷入僵局,因为 t运行sactional 变量的内容永远不会改变。
在我的代码中,Op
结构中的 v
变量是在一个线程中创建的,使用 t运行sactional 队列传递给另一个线程,然后由另一个线程使用,而且似乎没有理由在任何线程中对其进行垃圾收集。
因此我不清楚为什么这段代码会失败。
事务是原子的。问题出在这里:
r <- atomically $ do
writeTBQueue q (Op l v) -- (1)
takeTMVar v -- (2)
这将阻塞,除非另一个线程在 (1) 和 (2) 之间执行 putTMVar
。原子性阻止了这种情况。
在一笔交易中,您不能 "send information" 到另一笔交易,并期望从那笔交易中得到 "reply"。这将要求前一个事务(逻辑上)在后一个事务之前执行,反之亦然,这是不可能的。