问题 如何将parMap与monadic函数一起使用?


我有一个monadic函数getRate:

getRate :: String -> IO Double

我想将这个函数映射到String的列表上。通常,我会这样做:

mapM getRate ["foo", "bar"]

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

parMapM getRate ["foo", "bar"]

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

我能做什么?


7266
2018-02-09 23:59


起源



答案:


您应该使用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。


6
2018-02-10 00:23



这很完美 - 谢谢! - Bill


答案:


您应该使用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。


6
2018-02-10 00:23



这很完美 - 谢谢! - Bill


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


6
2018-06-24 13:25