如何创建线程池?

时间:2012-02-08 12:30:59

标签: haskell

有时我想同时为网络活动等并行运行最大量的IO操作。我启动了一个小的并发线程函数,它与https://gist.github.com/810920很好地协作,但这不是真的所有IO操作必须在其他人可以启动之前完成。

我正在寻找的类型是:

runPool :: Int -> [IO a] -> IO [a]

并且应该能够在有限和无限列表上运行。

管道包似乎能够很好地实现这一点,但我觉得可能有一个类似的解决方案,我只是使用来自haskell平台的mvars等提供的。

有没有人遇到过没有任何重依赖的惯用解决方案?

2 个答案:

答案 0 :(得分:7)

你需要一个线程池,如果你想要一些简短的东西,你可以从Control.ThreadPool(来自控制引擎包,它也提供更多通用功能)中获取灵感,例如threadPoolIO就是:

threadPoolIO :: Int -> (a -> IO b) -> IO (Chan a, Chan b)
threadPoolIO nr mutator = do
    input <- newChan
    output <- newChan
    forM_ [1..nr] $
        \_ -> forkIO (forever $ do
            i <- readChan input
            o <- mutator i
            writeChan output o)
    return (input, output)

它使用两个Chan与外部进行通信,但这通常是你想要的,它真的有助于编写不会搞砸的代码。

如果您绝对想要将其包装在您的类型的函数中,您也可以封装通信:

runPool :: Int -> [IO a] -> IO [a]
runPool n as = do
  (input, output) <- threadPoolIO n (id)
  forM_ as $ writeChan input
  sequence (repeat (length as) $ readChan output)

这不会保持您的操作顺序,这是一个问题(通过传输操作的索引或仅使用数组来存储响应很容易纠正)?

注意:使用这个简单版本,n个线程将永远保持活跃状态​​,如果您打算在长时间运行的应用程序中创建并删除其中的几个池,则向threadPoolIO添加“killAll”返回的操作可以轻松解决此问题(如果不是,考虑到Haskell中线程的重量,它可能不值得麻烦)。 请注意,此函数仅适用于有限列表,这是因为IO通常是严格的,因此您无法在生成整个列表之前开始处理IO [a]的元素,如果您真的希望您要么使用惰性带有unsafeInterleaveIO的IO(可能不是最好的主意)或者完全改变你的模型并使用管道来传输结果。

答案 1 :(得分:2)