使用Haskell的MVar包但强制执行seq

时间:2015-06-26 13:19:57

标签: multithreading haskell

根据有关hackage的Control.Concurrent.MVar文档,我们提供了一个' gotcha'关于MVar的用法。这是link

MVar说当你使用putMVar将某些内容放入MVar时,如果你投入的那个东西是一个巨大的东西,接收线程将会有一个混乱的评估工作,而不是发送线程。

除了可能令人烦恼或无根据之外,为了纠正这种情况,它指出了evaluate本身所说的使用seq的方向。每个人最喜欢的haskell功能。

Evaluate的语义应该是这样的:

evaluate x `seq` y    ==>  y

所以我的问题是:为什么要在分叉的线程中进行评估!?!?!

concTreeMap :: (a -> b) -> BinaryTree a -> IO (BinaryTree b)
concTreeMap f Leaf = return Leaf
concTreeMap f (Branch v l r) =  do
  res <- newEmptyMVar
  forkIO $ do
              let fv = f v
              evaluate fv `seq` (putMVar res fv)
  v' <- takeMVar res
  l' <- concTreeMap f l
  r' <- concTreeMap f r
  return (Branch v' l' r')

编辑添加等效的加速...

不知何故,这相当于下面的答案(不使用评估但使用seq)...无论如何,我认为加速的重点在于a)提供关于thunk评估的haskell运行时的提示和2)移动带走了

concTreeMap :: (a -> b) -> BinaryTree a -> IO (BinaryTree b)
concTreeMap f Leaf = return Leaf
concTreeMap f (Branch v l r) =  do
  res <- newEmptyMVar
  forkIO $ do { let fv = f v in fv `seq` putMVar res fv }
  l' <- concTreeMap f l
  r' <- concTreeMap f r
  v' <- takeMVar res
  return (Branch v' l' r')

2 个答案:

答案 0 :(得分:6)

假设你按照PetrPudlák的回答修改了你的程序,你试图在启动线程后立即从MVar取出一个值。所以你没有并行性,因为运行concTreeMap的线程必须等待MVar已满,这意味着等待分叉线程将fv放入MVar,它在评估之后才做到。与此同时,原始线程无所作为。

你不是要写

  ...
  forkIO $ do
              let fv = f v
              evaluate fv `seq` (putMVar res fv)
  l' <- concTreeMap f l
  r' <- concTreeMap f r
  v' <- takeMVar res         -- Note: this moved to after we do more work
  return (Branch v' l' r')

答案 1 :(得分:5)

因为您使用seq来强制评估evaluate fv。这是IO操作,评估正在运行完全不同。

如果您将IO操作视为一个配方,在其上运行seq,对其进行评估,只是确保配方完全写下来,但运行实际上是使用配方来生成东西。

分叉部分应该是

forkIO $ do
          let fv = f v
          evaluate fv
          putMVar res fv