2016-09-25 2 views
1

Die folgende scheint zu funktionieren (wie in: es Surely tomorrow jeder zweite hält sagen)

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 

Dies verwendet evaluate, um sicherzustellen, last [0..] tatsächlich zu WHFN gezwungen wird, bevor die MVar Füllung - wenn ich ändern, um die Gabel Thread zu

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

dann wird das Programm beendet.

Jedoch verwendet evaluateseq. Im Kontext der deterministischen Parallelität wird immer betont, dass seq nicht ausreicht, um tatsächlich die Bewertungsreihenfolge zu garantieren. Tritt dieses Problem nicht in einem monadischen Kontext entstehen, oder soll ich besser

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

die Compiler, um sicherzustellen, kann die Auswertung neu anordnen, so tryTakeMVar vorzeitig erfolgreich ist?

Antwort

0

Der Punkt von pseq ist sicherzustellen, dass nachdem der übergeordnete Thread eine Berechnung mit par funkt, es nicht sofort fortfährt, das Ergebnis der entflammten Berechnung selbst auszuwerten, sondern stattdessen seinen eigenen Job zuerst ausführt. Ein Beispiel finden Sie in the documentation. Wenn Sie expliziter mit Nebenläufigkeit arbeiten, sollten Sie pseq nicht benötigen.

1

Wenn ich nicht ganz falsch liege, würde die Auswertung von last [0..] zu WHNF eine unendliche Zeit in Anspruch nehmen, weil WHNF für eine Int bedeutet, dass Sie die genaue Anzahl kennen.

putMVar nicht ausgeführt wird starten, bevor last [0..] zu WHNF ausgewertet wird (die, wie wir wissen, immer nimmt), weil putMVar die durch den Aufruf zu evaluate zurück RealWorld -token (s) benötigen. (Oder um es einfacher: evaluate Werke Es endet erst nach dem Argumente WHNF auswertet..)

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

wo seq# eine GHC-prim-Funktion, die (# a, s #) nur zurück garantiert nach a zu WHNF Auswertung (das ist sein Sinn). Das heißt, nur nachdem a zu WHNF ausgewertet wurde, kann s in dem Anruf zu putMVar verwendet werden. Obwohl diese Token rein phantasievoll sind ("RealWorld ist zutiefst magisch ..."), werden sie vom Compiler respektiert, und die gesamte IO-Monade baut darauf auf.

Also ja, evaluate ist in diesem Fall genug. evaluate ist mehr als seq: es kombiniert IO-monadische Sequenzierung mit seq#-Sequenzierung, um seine Wirkung zu erzeugen.


In der Tat sieht die pseq Version nicht ganz geheuer zu mir, weil es hängt letztlich von lazy, wo evaluate hängt letztlich davon ab seq# und monadischen Token-Passing. Und ich vertraue seq# ein bisschen mehr.

Verwandte Themen