是评价还是$!足以WHNF强制多线程monadic上下文中的值,还是我需要pseq?

时间:2016-09-25 12:09:27

标签: haskell concurrency parallel-processing strictness

以下似乎有效(例如:它每秒都在说Surely tomorrow

import Control.Concurrent
import Control.Concurrent.MVar

import Control.Exception (evaluate)

main :: IO ()
main = do
    godot <- newEmptyMVar
    forkIO $ do
        g <- evaluate $ last [0..]
        putMVar godot g
    let loop = do
        threadDelay $ 10^6
        g <- tryTakeMVar godot
        case g of
            Just g -> return ()
            Nothing -> putStrLn "Surely tomorrow." >> loop
    loop

这使用evaluate确保last [0..]在填充MVar之前实际上被强制为WHFN - 如果我将分叉线程更改为

    forkIO $ do
        let g = last [0..]
        putMVar godot g

然后程序终止。

但是,evaluate使用seq。在确定性并行性的背景下,它始终强调seq不足以实际保证评估顺序。这个问题不会出现在monadic环境中,或者我应该更好地使用

    forkIO $ do
        let g = last [0..]
        g `pseq` putMVar godot g

确保编译器不能对评估进行重新排序,以便tryTakeMVar过早成功?

2 个答案:

答案 0 :(得分:1)

如果我并非完全错误,那么评估last [0..]到WHNF将花费无限的时间,因为Int的WHNF意味着您知道确切的数字。

putMVarlast [0..]被评估为WHNF之前不会开始执行(正如我们所知道的那样),因为putMVar将需要RealWorld - 令牌({{ 1}})通过调用s返回。 (或者更简单地说:evaluate有效。只有在评估其对WHNF的论证后才能完成。)

evaluate

其中evaluate :: a -> IO a evaluate a = IO $ \s -> seq# a s -- this ^ putMVar (MVar mvar#) x = IO $ \ s# -> -- which is used here ^^ case putMVar# mvar# x s# of -- is needed here ^^ s2# -> (# s2#, () #) 是一个GHC-prim函数,只有在评估seq#到WHNF之后才能保证返回(# a, s #)(这就是它的目的)。也就是说,只有在a评估为WHNF后,a才能用于s的调用。虽然这些令牌纯粹是富有想象力的(#34; RealWorld非常神奇......&#34;),它们受到编译器的尊重,并且整个IO-monad都建立在它之上。

所以是的,putMVar就足够了。 evaluate超过evaluate:它将IO-monadic排序与seq结合 - 排序以产生效果。

事实上,seq#版本对我来说有点可疑,因为它最终取决于pseq,其中lazy最终取决于evaluate和monadic令牌传递。我更信任seq#

答案 1 :(得分:0)

pseq的要点是确保在父线程用par激发计算之后,它不会立即尝试评估引发计算本身的结果,而是执行其先自己的工作。有关示例,请参阅the documentation。当您使用并发性更明确地工作时,您不应该需要pseq