forkIO
的文档说
GHC note: the new thread inherits the masked state of the parent (see mask).
The newly created thread has an exception handler that discards the exceptions
BlockedIndefinitelyOnMVar, BlockedIndefinitelyOnSTM, and ThreadKilled, and passes
all other exceptions to the uncaught exception handler.
为什么子异常处理程序会丢弃ThreadKilled
?两个线程在创建之后是否存在某种连接?
父线程死亡后究竟发生了什么?孩子是否得到任何例外?或者从孩子的角度来看,父母是否已经死了?除了父线程刚停止运行之外还有其他事情发生吗?
我之所以这么说是因为在很多情况下,我被迫在我无法访问父母范围的环境中创建一个帖子。想象一下你在图书馆的某个地方需要调用forkIO
,并在父母去世时让那个线程死掉。是否有必要重组程序并将孩子的ThreadId
传播给父母并明确杀死它?或者还有其他解决方法吗?
答案 0 :(得分:6)
父线程死亡后到底发生了什么?
无。 POSIX threads实际上也是如此。主题不会以C语言或类似语言的fork
分享您可能知道的亲子关系。但是,一个主线程,其终止通常会导致整个程序终止:
请注意,最初调用
main()
的线程与此不同。当它从main()
返回时,效果就像使用exit()
的返回值作为退出状态对main()
进行隐式调用一样。
孩子是否得到任何异常?或者从孩子的角度来看,父母是否已经死了?除了父线程停止运行之外还有其他事情发生吗?
没有。不,不。出于与通常的OS线程相同的原因。你可以很容易地尝试这个:
import Control.Concurrent (forkIO, threadDelay)
delaySeconds n = threadDelay $ n * (10^6)
main = do
forkIO $ do
forkIO $ delaySeconds 1 >> print "Pseudo child 1"
forkIO $ delaySeconds 1 >> print "Pseudo child 2"
print "Pseudo parent says goodbye"
delaySeconds 10
print "Exiting main"
父母"会说再见,而且孩子们会#34;将在稍后打印。请记住,线程编程中没有实际的父级。只有兄弟姐妹。其中一个有点特别,是的,但这就是如何指定的。
是否有必要重组程序并将孩子的
ThreadId
传播给父母并明确杀死它?
至少一点点,因为forkIO
没有提供此功能。另外,如果有forkIOKillAutomatically
,它应该有什么类型?为什么?
或者还有其他解决方法吗?
好吧,您可以将parent
的其余部分作为另一个操作提供,因此请使用帮助程序:
forkRunDie :: IO () -> IO () -> IO ()
forkRunDie p s = forkIO p >>= \tid -> s >> killThread tid
上面的例子将成为
main = do
forkIO $ do
forkRunDie (delaySeconds 1 >> print "Pseudo child 1") $ do
forkRunDie (delaySeconds 1 >> print "Pseudo child 2") $ do
print "Pseudo parent says goodbye"
delaySeconds 10
print "Exiting main"
在这种情况下,唯一的输出将是
"Pseudo parent says goodbye"
"Exiting main"
另见:
forkRunDie
提供与finally
非常类似的功能。答案 1 :(得分:3)
在独立的GHC程序中,只有主线程需要终止才能使进程终止。因此,所有其他分叉线程将简单地与主线程同时终止(这种行为的术语是“守护线程”)。
https://hackage.haskell.org/package/base-4.7.0.0/docs/Control-Concurrent.html#g:12
答案 2 :(得分:0)
这是一个受Zeta启发的答案。它使用一个免费的monad变换器来避免计算的显式嵌套,以及来自async
包而不是forkRunDie
的{{3}}函数。
module Main where
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Free (FreeT,liftF,iterT)
import Control.Concurrent
import Control.Concurrent.Async (withAsync)
import Control.Exception
type DaemonIO = FreeT ((,) (IO ())) IO
launch :: IO () -> DaemonIO ()
launch a = liftF (a,())
runDaemonIO :: DaemonIO a -> IO a
runDaemonIO = iterT $ \(action,rest) -> withAsync action $ \_ -> rest
main :: IO ()
main = do
let delaySeconds n = threadDelay $ n * (10^6)
runDaemonIO $ do
launch $ (forever $ delaySeconds 1 >> print "Pseudo child 1")
`finally` putStrLn "killed 1!"
launch $ (forever $ delaySeconds 1 >> print "Pseudo child 2")
`finally` putStrLn "killed 2!"
liftIO $ delaySeconds 10
liftIO $ putStrLn "done!!!"