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事务中涉及的变量以某种方式被垃圾收集,只能在retry的单个线程中看到,并因此被锁定,因为事务变量的内容不会改变。

在我的代码中, Op结构中的v变量在一个线程中创建,使用事务性队列传递给另一个线程,然后由另一个线程使用,并且似乎没有理由在任何线程中进行垃圾回收。

因此,我不清楚为什么这个代码正在失败。


事务是原子的。 问题在于:

r <- atomically $ do
        writeTBQueue q (Op l v) -- (1)
        takeTMVar v             -- (2)

这将阻塞,除非另一个线程在(1)和(2)之间执行putTMVar 。 原子性阻止了这一点。

在交易中,您不能“发送信息”到另一个交易,并期望从中获得“回复”。 这将要求前一笔交易在后一笔交易之前(逻辑上)执行,反之亦然,这是不可能的。

链接地址: http://www.djcxy.com/p/81463.html

上一篇: What is the precise reason I got blocked on STM?

下一篇: Haskell STM and retry