並行スレッドマネージャーの次の実装があります
newtype Query = Query String
type ThreadWorker = (Query, ThreadStatus)
data ThreadStatus = Running | Finished | Threw IOException
newtype ThreadManager = Manager (MVar (M.Map ThreadId (MVar ThreadWorker)
manageWorkers :: ThreadManager -> IO ()
を横断し、のをMap
見るものを書きたいと思います。ThreadStatus
ThreadWorker
が終了するThreadWorker
と、から削除されThreadManager
ます。例外がスローされた場合は、それを処理する必要があり(たとえば、stdoutへの出力は問題ありません)、クエリを処理するために新しいスレッドをフォークして(関数の存在を想定) 、それ以外の場合はスレッドrunQuery :: Query -> IO a
に追加する必要があります。ThreadManager
はまだ実行中であり、そのままにしておく必要があります。
私が最初に実装を試みたのは次のとおりです。
manageWorkers :: ThreadManager -> IO ()
manageWorkers (Manager mgr) =
modifyMVar mgr $ \m -> do
m' <- M.traverseWithKey manageWorker m
return (m', ())
where manageWorker :: ThreadId -> MVar ThreadWorker -> IO (MVar ThreadWorker)
manageWorker tid wkr = tryTakeMVar wkr >>= \mwkr ->
case mwkr of
Just (_, Finished) -> undefined -- need to delete this finished ThreadWorker
Just (q, Threw e ) -> do
putStrLn ("[ERROR] " ++ show e)
tid' <- forkIO $ runQuery q
undefined -- need to add new ThreadWorker
Just r -> newMVar r
_ -> newEmptyMVar
しかし、それから私は立ち往生しました、ThreadManager
中から/に削除または追加することは不可能のようですmanageWorker
。traverse
のような関数からやりたいことができるかどうかわかりません。
manageWorkers
私を使用してこの関数を実装することは可能ですか、ThreadManager
それともより良い抽象化がありますか?
編集:ThomasM.DuBuissonのフォールドの使用の提案で、私は今、次のようになっています
manageWorkers (Manager mgr) =
modifyMVar mgr $ \m ->
return (M.foldrWithKey manageWorker M.empty m, ())
where manageWorker :: ThreadId -> MVar ThreadWorker -> M.Map ThreadId (MVar ThreadWorker)
-> IO (M.Map ThreadId (MVar ThreadWorker))
manageWorker tid wkr ts = tryTakeMVar wkr >>= \mwkr ->
case mwkr of
Just (q, Threw e) -> do
putStrLn ("[ERROR] " ++ show e)
wkr' <- newEmptyMVar
tid' <- forkIO $ runQuery q
return $ M.insert tid' wkr' ts
Just (_, Running) -> return $ M.insert tid wkr
_ -> return ts
唯一の問題は、明らかにmanageWorker
の署名がで機能しないことM.foldrWithKey
です。必要なのM.foldrWithKeyM :: Monad m => (k -> a -> b -> m b) -> b -> M.Map k a -> m b
ですが、そんなものは存在せず、自分で作曲するのに苦労しています。
明らかにunsafePerformIO
、IOモナドをエスケープしてコンパイラーを満足させるために使用できますが、それは最後の手段としてのみ使用します。これは使用するのが理にかなっている状況unsafePerformIO
ですか?