Как я могу использовать parMap с одноместной функцией?

У меня есть одноместная функция getRate:

getRate :: String -> IO Double

Я хотел бы отобразить эту функцию по списку Строки. Обычно, я просто сделал бы:

mapM getRate ["foo", "bar"]

но так как каждый вызов к getRate выполняет сетевые вызовы, я хотел бы параллелизировать карту так, чтобы каждый уровень был выбран в отдельном потоке (или по крайней мере распространен среди очередей). Я думаю о чем-то как

parMapM getRate ["foo", "bar"]

но нет никакой функции parMapM, и parMap не работает с одноместными функциями.

Что я могу сделать?

12
задан Don Stewart 18 April 2011 в 22:41
поделиться

2 ответа

Вы должны использовать 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 проходит через ожидание каждого потока по очереди. Но ввод-вывод происходит параллельно.

Обратите внимание, что если вам нужно вызывать посторонние функции, вы должны использовать forkOS вместо forkIO.

7
ответ дан 2 December 2019 в 21:23
поделиться

Существует также пакет с монадным параллелизмом, который предоставляет mapM :: MonadParallel m => (a -> m b) -> [a] -> m [b] . Глядя на экземпляр IO для MonadParallel, он делает это так же, как в ответе Доминика.

6
ответ дан 2 December 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: