Haskell并发 - forkIO真的不确定吗?

时间:2012-04-20 13:47:24

标签: haskell concurrency

我正在尝试在Haskell中使用并发进行特定优化,其中只需要两个值中的一个,并且根据情况,任何一个可能比另一个快得多。

我以为我可以用forkIO运行2个线程,然后等到一个值放在一个MVar中。这是我为此写的一个简单的测试:

import Control.Concurrent

main = do out <- newEmptyMVar
          t1 <- forkIO (makeString out)
          t2 <- forkIO (makeInt out)
          v <- takeMVar out
          killThread t1
          killThread t2
          case v of
               Left s -> putStrLn s
               Right i -> putStrLn $ show i

makeString out = do s <- return ( show (primes !! 10000))
                    putMVar out $ Left s

makeInt out = do i <- return 2
                 putMVar out $ Right i

primes = sieve [2..] 
 where sieve (x:xs) = x : (sieve $ filter ((/=0).(flip mod x)) xs)

编译:

ghc --make -threaded Test

然而,只有Left的情况才会到达,虽然获取素数应该花费足够长的时间来启动makeInt线程(并且返回2确实不应该花费那么多时间)。为什么会这样,我该如何解决这个问题?

2 个答案:

答案 0 :(得分:20)

这里的问题是懒惰。 makeString只是插入一个thunk来计算show (primes !! 10000),然后由主线程进行评估。插入thunk的速度非常快,所以在这种情况下碰巧赢得比赛。

要强制在线程内进行评估,您可以将return更改为evaluate

makeString out = do s <- evaluate $ show (primes !! 10000)
                    putMVar out $ Left s

在大多数情况下,这会导致makeInt赢得比赛(尽管不能保证)。

答案 1 :(得分:10)

是的,线程确实是非确定性的(在GHC中)。

只是发生了你的特定代码的结构和优化,使得t1总是获胜。没有保证。

如果您想尝试按摩它以产生不同的结果,请尝试启用优化(-O2)和/或使用多个核心(+RTS -N)。

E.g。在我的机器上,连续两次运行:

$ ghc -O2 -threaded --make A.hs -rtsopts -fforce-recomp
[1 of 1] Compiling Main             ( A.hs, A.o )
Linking A.exe ...
$ ./A +RTS -N2
2
$ ./A +RTS -N2
104743

正如hammar指出的那样,您还可以构建代码以强制在线程中进行更多工作(或切换到using strict mvars)。