Multithreading 如何将父异步与多个子异步链接
的文档将Multithreading 如何将父异步与多个子异步链接,multithreading,haskell,asynchronous,concurrency,Multithreading,Haskell,Asynchronous,Concurrency,的文档将withAsync功能描述为: 在单独的线程中生成异步操作,并传递其异步 提供的函数的句柄。当函数返回或抛出 在异步服务器上调用了一个异常,即不间断取消。这是一个 async的有用变体,确保async永远不会停止运行 无意中 在过去的两个小时里,我一直在关注这一点,但一直没有弄清楚如何启动一个监视器线程,该线程会产生多个工作线程,例如: 如果监视器线程死亡,则应终止所有工作线程 但是,如果任何工作线程死亡,其他工作线程都不会受到影响。应该通知监视器,并且它应该能够重新启动工作线程 我们
withAsync
功能描述为:
在单独的线程中生成异步操作,并传递其异步
提供的函数的句柄。当函数返回或抛出
在异步服务器上调用了一个异常,即不间断取消。这是一个
async的有用变体,确保async永远不会停止运行
无意中
在过去的两个小时里,我一直在关注这一点,但一直没有弄清楚如何启动一个监视器线程,该线程会产生多个工作线程,例如:
- 如果监视器线程死亡,则应终止所有工作线程
- 但是,如果任何工作线程死亡,其他工作线程都不会受到影响。应该通知监视器,并且它应该能够重新启动工作线程
- 我们似乎需要两个功能:一个启动所有异步任务,另一个监视它们并在它们死时重新启动
第一个可以这样写:
withAsyncMany :: [IO t] -> ([Async t] -> IO b) -> IO b
withAsyncMany [] f = f []
withAsyncMany (t:ts) f = withAsync t $ \a -> withAsyncMany ts (f . (a:))
import Control.Monad.Managed (with,managed)
withAsyncMany' :: [IO t] -> ([Async t] -> IO b) -> IO b
withAsyncMany' = with . traverse (\t -> managed (withAsync t))
如果我们使用这个包,我们也可以这样写:
withAsyncMany :: [IO t] -> ([Async t] -> IO b) -> IO b
withAsyncMany [] f = f []
withAsyncMany (t:ts) f = withAsync t $ \a -> withAsyncMany ts (f . (a:))
import Control.Monad.Managed (with,managed)
withAsyncMany' :: [IO t] -> ([Async t] -> IO b) -> IO b
withAsyncMany' = with . traverse (\t -> managed (withAsync t))
restart函数将循环异步列表,轮询它们的状态,并在它们失败时更新它们:
{-# language NumDecimals #-}
import Control.Concurrent (threadDelay)
resurrect :: IO t -> [Async t] -> IO ()
resurrect restartAction = go []
where
go ts [] = do
threadDelay 1e6 -- wait a little before the next round of polling
go [] (reverse ts)
go past (a:pending) = do
status <- poll a -- has the task died, or finished?
case status of
Nothing -> go (a:past) pending
Just _ -> withAsync restartAction $ \a' -> go (a':past) pending
{-#语言NumDecimals}
导入控制。并发(线程延迟)
复活::IO t->[Async t]->IO()
重新启动操作=继续[]
哪里
去做某事
threadDelay 1e6--在下一轮轮询之前稍等片刻
go[](反向ts)
通过(a:待定)=完成
状态go(a:过去)待定
Just->withAsync RestarAction$\a'->go(a):过去)挂起
然而,我担心许多嵌套的withAsync
可能会导致某种类型的资源泄漏(因为必须为每个withAsync
安装某种类型的异常处理程序,以便在父线程死亡时通知子线程)
因此,在这种情况下,最好使用纯
async
s生成worker,将async
s的集合存储到某种可变引用中,并在监视器线程中安装一个异常处理程序,它将遍历终止每个任务的容器,使用async
而不是withAsync
。主要功能是
monitor :: Int -> IO () -> IO ()
monitor count task =
bracket (do asyncs <- replicateM count (async task)
newIORef asyncs)
(\ref -> forever (do
threadDelay 1e6
asyncs <- readIORef ref
vivify task (writeIORef ref) asyncs))
(\ref -> do
asyncs <- readIORef ref
mapM_ uninterruptibleCancel asyncs)
我们在创建新的
Async
和将其“持久化”到IOref
之间的时间间隔内屏蔽异步异常,因为否则,如果异步异常在这两者之间到达并终止监视线程,那么Async
将保持悬空状态 好奇:你的用例是什么?听起来您正试图构建一个Erlang式的监管模型,对于该模型,像@BenjaminHodgson这样的参与者库可能会更好地为您服务。该用例是在我的webapp启动时生成一个作业队列。作业队列在内部生成作业轮询线程和作业侦听/通知线程。如果这些线程中的任何一个死亡,则需要重新生成它们。如果应用程序线程被终止,则需要终止作业队列。