我正在尝试制定 Haskell 的异步模型,但在将已知概念与 Haskell 所做的相匹配时遇到了麻烦。 我有以下代码:
module Main where
import Control.Concurrent.Async (concurrently)
main :: IO ()
main = do
putStrLn "Hello, Haskell!"
(a, b) <- concurrently (pure $ myFibonaci 45) (pure $ myFibonaci 42)
print a
putStrLn "----"
print b
myFibonaci :: Integer -> Integer
myFibonaci 0 = 0
myFibonaci 1 = 1
myFibonaci n = myFibonaci (n - 1) + myFibonaci (n - 2)
不知道为什么,但日志按以下顺序打印:
267914296 -- value of a
----
然后才开始计算 b 的值
267914296
----
102334155 -- value of b
我有点困惑,因为根据我的理解,
concurrently
应该产生2个绿色线程,这些线程又可以映射到不同的系统线程并最终在不同的内核上执行。这意味着当我得到 a
的值时,理论上应该已经计算出 b
的值(如果没有调度延迟)。我正在使用 -threaded
GHC 选项运行代码,所以这不应该是问题。这是 Haskell 懒惰的情况吗?我怀疑更好的解决方案是使用 Control.Parallel
但是 async
和 Control.Parallel
之间有什么区别呢?我很确定使用类似模型的 Go 不会做出这样的区别
这是由于懒惰造成的。您正在执行的
IO
操作是 pure
,而不是 myFibonaci xy
,因此在 (a, b) <- ...
行,元组的两个元素都是在打印时评估的 thunk。使用@jonpurdy的解决方案:$!
。需要考虑两件事:
Control.Parallel
API,将迫使您通过 rseq
、rdeepseq
等选择并行执行所需的“惰性级别”。下面有代码的修复和
strategies
方法
import Control.Concurrent.Async (concurrently)
import Data.Time.Clock (getCurrentTime)
import Control.Exception
import Control.Parallel.Strategies
-- just an utility
measure :: Show a => a -> IO ()
measure a = do
getCurrentTime >>= print
print a
getCurrentTime >>= print
main :: IO ()
main = do
putStrLn "Hello, Haskell!"
-- if you change $! by $ you'll notice measure b is much longer because
-- computation is done on measure b but with $!, both measure are the same
-- since the calculation is done here
(a, b) <- concurrently (pure $! myFibonaci 15) (pure $! myFibonaci 35)
measure a
putStrLn "----"
measure b
putStrLn "********"
getCurrentTime >>= print
-- strategies interface force you to choose the level of lazyness
-- |- eval in IO in parallel
-- | |- this pure computation
-- | | |- with strategy "parallel tuples with full evaluation of each side of the tuple"
result <- usingIO (myFibonaci 14, myFibonaci 34) (evalTuple2 rdeepseq rdeepseq)
print result
getCurrentTime >>= print
myFibonaci :: Integer -> Integer
myFibonaci 0 = 0
myFibonaci 1 = 1
myFibonaci n = myFibonaci (n - 1) + myFibonaci (n - 2)