以下似乎有效(例如:它每秒都在说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
过早成功?
答案 0 :(得分:1)
如果我并非完全错误,那么评估last [0..]
到WHNF将花费无限的时间,因为Int
的WHNF意味着您知道确切的数字。
putMVar
在last [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
。