Haskell 异步中纯代码的并行执行

问题描述 投票:0回答:1

我正在尝试制定 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 不会做出这样的区别

multithreading asynchronous haskell parallel-processing
1个回答
0
投票

这是由于懒惰造成的。您正在执行的

IO
操作是
pure
,而不是
myFibonaci xy
,因此在
(a, b) <- ... 
行,元组的两个元素都是在打印时评估的 thunk。使用@jonpurdy的解决方案:
$!
。需要考虑两件事:

  • Control.Parallel
    API,将迫使您通过
    rseq
    rdeepseq
    等选择并行执行所需的“惰性级别”。
  • 异步代码与并行代码不同。前者可以在一个内核中运行(并且很可能应该运行),进行某种上下文切换,后者必须在多个内核中运行。我推荐 S.Marlow 的 haskell 中的并行和并发编程,他是为 haskell 编写 par/conc 运行时的人。

下面有代码的修复和

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)
© www.soinside.com 2019 - 2024. All rights reserved.