2012-02-08 14 views
5

時々、ネットワークアクティビティなどのために一度に最大限のIOアクションを並列に実行したい場合は、https://gist.github.com/810920でうまくいく小さな同時スレッド機能を利用しましたが、実際にはすべてのIOアクション他人が始める前に終わらなければならない。スレッドプールを作成するにはどうすればよいですか?

私が探しているものの種類は次のようなものになるだろう:

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

と有限と無限リストを操作することができるはずです。

このパイプパッケージは、これを非常にうまくいくように見えますが、おそらく私がhaskell-platformからmvarsなどを使って提供した要点と同様の解決策があると思います。

大きな依存関係のない慣用的な解決策を見つけた人はいますか?

答えて

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) 

これは2つの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個のスレッドは永久に生き続けることになりますが、「killAll」という返されたアクションをthreadPoolIOに追加すると、長時間実行しているアプリケーションでプールをいくつか作成し、ハスケルのスレッドの重さを考えれば、恐らくそれほど価値がないでしょう)。 この関数は有限リスト上でのみ機能します.IOは通常は厳密であるため、リスト全体が生成される前にIO [a]の要素を処理することはできません。 (おそらく最善のアイデアではない)unsafeInterleaveIOを使って遅延IOを実行するか、モデルを完全に変更して、結果をストリームするためのコンジットなどを使用します。

+1

runPoolタイプに執着していないと、threadPoolIO自体はもう少し堅牢です。作成したプログラムのいくつかの場所で簡単に再利用でき、無限リストを分割してフィードし、チャンクなどによる応答... – Jedai

+0

'threadPoolIO'は正常に見えます。私はスレッドプールを作成する最も良い方法に興味があり、どのHackageバージョンがコミュニティに好まれているかを知っているので、これがどのように実装されているかを見るためにコードを見ていきます。 –

関連する問題