あなたがスレッドプールを必要とする、あなたが短い何かをしたい場合、あなたは(また、より一般的な機能を提供制御エンジンパッケージから)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を実行するか、モデルを完全に変更して、結果をストリームするためのコンジットなどを使用します。
runPoolタイプに執着していないと、threadPoolIO自体はもう少し堅牢です。作成したプログラムのいくつかの場所で簡単に再利用でき、無限リストを分割してフィードし、チャンクなどによる応答... – Jedai
'threadPoolIO'は正常に見えます。私はスレッドプールを作成する最も良い方法に興味があり、どのHackageバージョンがコミュニティに好まれているかを知っているので、これがどのように実装されているかを見るためにコードを見ていきます。 –