【问题标题】:What is the precise reason I got blocked on STM?我在 STM 上被屏蔽的确切原因是什么?
【发布时间】:2016-05-22 15:50:55
【问题描述】:

我有以下 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 事务中涉及的变量已以某种方式被垃圾收集,仅在 retrying 的单个线程中可见,因此由于事务变量的内容而死锁永远不会改变。

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

因此我不清楚为什么这段代码会失败。

【问题讨论】:

    标签: multithreading haskell concurrency stm


    【解决方案1】:

    事务是原子的。问题出在这里:

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

    这将阻塞,除非另一个线程在 (1) 和 (2) 之间执行 putTMVar。原子性可以防止这种情况发生。

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

    【讨论】:

      猜你喜欢
      • 2016-02-14
      • 2016-04-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-19
      • 2017-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多