2016-09-25 9 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 для обеспечения 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 преуспевает преждевременно?

ответ

0

Пункт pseq состоит в том, чтобы гарантировать, что после того, как родительский поток искроет вычисление с помощью par, он не сразу начнет оценивать результат искрового вычисления, а вместо этого выполняет свое собственное задание первым. См., Например, the documentation. Когда вы работаете более явно с параллелизмом, вам не нужно pseq.

1

Если я не совсем ошибаюсь, оценка last [0..] WHNF потребует бесконечного количества времени, потому что WHNF для Int означает, что вы знаете точное число.

putMVar не начнет выполняться до last [0..] оценивается в WHNF (который, как мы знаем, принимает навсегда), потому что 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, s #) только после оценки a в WHNF (это его цель). То есть, только после того, как a оценивается в WHNF, s может использоваться при вызове putMVar. Хотя эти жетоны являются чисто воображаемыми («RealWorld глубоко магический ...»), они соблюдаются компилятором, и вся IO-монада построена поверх нее.

Так что да, evaluate достаточно в этом случае. evaluate - более seq: он сочетает в себе IO-монадическое секвенирование с seq#-sequencing, чтобы произвести его эффект.


На самом деле, версия pseq выглядит немного подозрительной для меня, потому что в конечном счете, зависит от того, где lazyevaluate в конечном счете, зависит от seq# и монадической передачи маркеров. И я доверяю seq# еще немного.