12

我有一个单子函数getRate:

getRate :: String -> IO Double

我想将此函数映射到字符串列表。通常,我会这样做:

mapM getRate ["foo", "bar"]

但由于每次调用 getRate 都会进行网络调用,我想并行化地图,以便在单独的线程中获取每个速率(或至少在队列中展开)。我在想类似的东西

parMapM getRate ["foo", "bar"]

但是没有 parMapM 函数,并且 parMap 不适用于 monadic 函数。

我能做些什么?

4

2 回答 2

7

您应该使用 Control.Concurrent 并围绕 Control.Concurrent.MVar 进行同步;就像是:

fork1 :: (a -> IO b) -> a -> IO (MVar b)
fork1 f x =
  do
    cell <- newEmptyMVar
    forkIO (do { result <- f x; putMVar cell result })
    return cell

fork :: (a -> IO b) -> [a] -> IO [MVar b]
fork f = mapM (fork1 f)

join :: [MVar b] -> IO [b]
join = mapM takeMVar

forkJoin :: (a -> IO b) -> [a] -> IO [b]
forkJoin f xs = (fork f xs) >>= join

这部分(fork,join)看起来是连续的。在实践中发生的情况是线程在 fork 中顺序触发,并且集合点依次遍历等待每个线程。但是 IO 是同时发生的。

请注意,如果您需要调用外部函数,您应该使用forkOS而不是 forkIO。

于 2010-02-10T00:23:18.633 回答
6

还有一个 monad-parallel 包,它提供mapM :: MonadParallel m => (a -> mb) -> [a] -> m [b]。查看 MonadParallel 的 IO 实例,它的执行方式与 Dominic 的回答相同。

于 2010-06-24T13:25:04.327 回答