使用TChan的类似管道的操作

时间:2015-06-08 15:33:16

标签: multithreading haskell stm

我想在两个线程之间实现一个管道。我有线程A获取数据,处理它,并将其发送到线程B.我有一个MVar,检查数据是否完全处理

但是,我有例外*** Exception: thread blocked indefinitely in an STM transaction

为什么我的线程被阻止了?我虽然比第一个线程在通道上写入时,然后当通道上有数据时,第二个可以读取它

fstPipe :: (a -> b) -> TChan b -> MVar () -> [a] -> IO ()
fstPipe f chIn m xs = do
    ( mapM_(\x-> atomically $ writeTChan chIn $ f x) xs) >> putMVar m ()

pipelineDone channel mIn = do
    isDone <- fmap isJust $ tryTakeMVar mIn
    isEmpty <- atomically $ isEmptyTChan channel
    return $ isDone && isEmpty

lastPipe f chIn mIn = iter 
    where iter = do
        atomically $ fmap f $ readTChan chIn
        isDone <- pipelineDone chIn mIn
        unless isDone $ iter

pipeline = do
    chIn <- atomically newTChan
    m <- newEmptyMVar
    first <- async $ fstPipe reverse chIn m $ replicate 10 [1..500]
    last <- async $ lastPipe print chIn m
    wait first
    wait last

3 个答案:

答案 0 :(得分:3)

在同一代码块中使用STM 信号量对我来说似乎很奇怪...为什么不在STM中执行整个操作?

特别是,为什么不TChan (Maybe x)Nothing表示序列的结束?

另外,请注意,您的fstPipe可能只会生成一堆未评估的thunk并立即将它们放入TChan,而不会实际计算任何内容。您可能希望seq或类似内容强制某个实际的工作在该线程上发生。

答案 1 :(得分:2)

我认为存在竞争条件:

  • fstPipe
  • 之前停止putMVar
  • 提前lastPipe阅读所有内容,然后致电pipelineDone
  • pipelineDone返回False,因为putMVar尚未完成
  • lastPipe会尝试阅读频道
  • putMVar执行,但为时已晚

现在lastPipe在空白频道上停留阅读。

答案 2 :(得分:1)

您的问题符合pipelineDone的逻辑。目前,您有:

pipelineDone channel mIn = do
  isDone <- fmap isJust $ tryTakeMVar mIn
  isEmpty <- atomically $ isEmptyTChan channel
  return $ isDone && isEmpty

tryTakeMVar将采用MVar的内容,假设其中有某些内容。假设您的制作人首先完成,它将把()写入MVar。然后,您的消费者将尝试获取其内容。如果成功,则MVar变空。任何后续tryTakeMVar将始终返回Nothing,因此isDone && isEmpty将始终返回false,您将继续尝试从TChan读取。一旦TChan变空,GHC就可以告诉你它遇到了死锁。

您应该将pipelineDone实现更改为:

pipelineDone channel mIn = do
  stillRunning <- isEmptyMVar mIn
  isEmpty <- atomically $ isEmptyTChan channel
  return $ (not stillRunning) && isEmpty

这将简单地轮询MVar,而不是实际清空它。