Multithreading 当其父线程在GHC Haskell中死亡时,子线程会发生什么情况?
Multithreading 当其父线程在GHC Haskell中死亡时,子线程会发生什么情况?,multithreading,haskell,ghc,Multithreading,Haskell,Ghc,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
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
传播到父级并显式终止它?或者还有其他解决办法吗
在独立GHC程序中,只有主线程需要终止,进程才能终止。因此,所有其他分叉线程将与主线程同时终止(这种行为的术语是“守护线程”)
当父线程死亡时,会发生什么
没什么。事实上,这也是事实。线程不共享您在C或类似语言中可能知道的父子关系。但是,有一个主线程,它的终止通常会导致整个程序的终止:
请注意,最初调用main()
的线程与此不同。当它从main()
返回时,效果就像使用main()
的返回值作为退出状态隐式调用了exit()
孩子有没有遇到任何异常?或者,从孩子的角度来看,父母是否已经去世了?除了父线程停止运行之外,还会发生其他情况吗
没有。没有。没有。原因与通常的操作系统线程相同。你可以很容易地尝试这个:
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"
“家长”会说再见,“孩子”会在第二天打印出来。请记住,线程编程中没有实际的父线程。只有兄弟姐妹。其中一个有点特别,是的,但这正是它被指定的方式
是否有必要重新构造程序并将子级的ThreadId
传播到父级并显式终止它
至少有一点,因为forkIO
不提供此功能。另外,如果有一个forkIOKillAutomatically
,它应该有什么类型?为什么
或者还有其他解决办法吗
那么,您可以将您的家长的其余部分作为另一个操作提供,并因此使用助手:
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"
在这种情况下,唯一的输出是
“伪家长说再见”
“正在退出主管道”
另见:
- Conal Elliott(提供了一个与
forkRunDie
非常相似的功能,带有finally
)
这是一个受泽塔启发的答案。它使用一个免费的monad转换器来避免显式嵌套计算,并使用
async
包中的函数代替forkRunDie
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!!!"
但是如果线程是从另一个线程分叉的,而不是从主线程分叉的呢?其他线程似乎也不会自动在GHCi中终止。有一个问题:您的分叉计算是否会向父线程返回值?否,它们永远运行