如何在单子函数中使用parMap?

12

我有一个单子函数getRate:

getRate :: String -> IO Double

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

mapM getRate ["foo", "bar"]

但由于每次调用getRate都会进行网络调用,因此我希望可以并行处理map,以便每个利率在单独的线程中获取(或者至少在队列中分散)。我正在考虑类似于以下的东西

parMapM getRate ["foo", "bar"]

但没有parMapM函数,而parMap无法用于单子函数。

我该怎么办?

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中线程是按顺序依次启动的,而rendezvous则在等待每个线程时依次进行。但IO是并发发生的。

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


6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接