2016-09-25 14 views
1

以下似乎工作(如:口口聲聲說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確保填充MVar之前last [0..]實際上是被迫WHFN - 如果我的分叉線程切換到

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

然後程序終止。

但是,evaluate使用seq。在確定性並行性方面,總是強調seq不足以實際保證評估順序。是否在一元範圍內不會出現這個問題,或者我應該更好地利用

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

,以確保編譯器不能重新排序評估,使tryTakeMVar成功過早?

回答

0

pseq的目的是爲了確保在父線程使用par觸發計算後,它不會立即着手評估啓動計算本身的結果,而是首先執行自己的工作。一個例子見the documentation。當你更明確地使用併發時,你不應該需要pseq

1

如果我沒有完全錯誤,那麼評估last [0..] WHNF將花費無限的時間,因爲WHNF爲Int意味着您知道確切的數字。前last [0..]進行評估,以WHNF(正如我們知道需要永遠)

putMVar不會開始執行,因爲putMVar將需要RealWorld -token(s)通過調用evaluate返回。 (或者說得更乾脆:evaluate作品它結束只評估它的參數WHNF後。)

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

其中seq#是GHC,整潔的功能,保證只評估a到WHNF後返回(# a, s #)(這是其目的)。也就是說,只有在a被評估爲WHNF之後,s纔可用於致電putMVar。儘管這些令牌純粹是富有想象力的(「真實世界是深奧的魔法......」),但它們受到編譯器的尊重,整個IO-monad都建立在它之上。

所以是的,在這種情況下evaluate就足夠了。 evaluate大於seq:它將IO-單子排序與seq#-排序相結合以產生其效果。


事實上,pseq版本看起來有點腥給我,因爲這最終取決於lazy,其中evaluate最終取決於seq#和一元令牌傳遞。我相信seq#更多一點。

相關問題