在有关hackage的Control.Concurrent.MVar文档下,我们有一个有关MVar用法的“陷阱”。这是link

MVar说,当您使用putMVar将某些内容放入MVar中时,如果要放入的内容很大,则接收线程将进行困惑的评估工作,而不是发送线程。

除了潜在的烦人或不必要的补救措施,要纠正这种情况,它还使我们指向了evaluate的方向,而seq本身就说要使用ojit_code。每个人最喜欢的haskell函数。

Evaluate的语义据说应该是这样的:



所以我的问题是:为什么这不会在分支线程中求值!

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)向haskell运行时提供有关thunk评估的提示,以及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')

最佳答案

假设您按照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')

09-26 04:43