2

以下似乎有效(如:它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..]在填充之前实际上强制 WHFN MVar- 如果我将分叉线程更改为

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

然后程序终止。

但是,evaluate使用seq. 在确定性并行的上下文中,总是强调这seq不足以真正保证评估顺序。这个问题不会出现在单子上下文中,还是我应该更好地使用

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

以确保编译器不能重新排序评估因此tryTakeMVar过早成功?

4

2 回答 2

1

的重点pseq是确保在父线程使用 触发计算后par,它不会立即继续尝试评估触发计算本身的结果,而是先完成自己的工作。有关示例,请参阅文档。当您更明确地使用并发时,您不需要pseq.

于 2016-09-25T21:25:41.707 回答
1

如果我没有完全错,评估last [0..]WHNF 将花费无限的时间,因为 WHNFInt意味着您知道确切的数字。

putMVar在被评估为 WHNF之前不会开始执行last [0..](我们知道这需要永远),因为putMVar将需要调用返回的RealWorld-token ( ) 。(或者更简单地说:有效。它仅在评估其对 WHNF 的参数后完成。)sevaluateevaluate

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#, () #)

whereseq#是一个 GHC-prim 函数,它保证(# a, s #)仅在评估a为 WHNF 后才返回(这就是它的目的)。也就是说,只有 aftera被评估为 WHNF,s才能在对 的调用中使用putMVar。尽管这些标记纯粹是想象的(“RealWorld 非常神奇......”),但它们受到编译器的尊重,整个 IO-monad 都建立在它之上。

所以是的,evaluate在这种情况下就足够了。evaluateis more than seq: 它结合了 IO-monadic 排序和seq#-sequencing 来产生它的效果。


事实上,这个pseq版本对我来说看起来有点可疑,因为它最终取决于lazyevaluate最终取决于seq#和 monadic 令牌传递。我seq#更相信一点。

于 2016-09-25T18:00:57.907 回答