2016-05-22 45 views
0

我这是为了实现一些基于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事务中涉及的变量已以某种方式被垃圾收集,仅在单个线程中可见,并且因此被锁定,因为事务变量的内容获胜永远不会改变。

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

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

回答

3

交易记录是原子。问题在于此处:

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

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

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

+0

当然。我似乎总是忘记...... – insitu

相关问题