Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 当其父线程在GHC Haskell中死亡时,子线程会发生什么情况?_Multithreading_Haskell_Ghc - Fatal编程技术网

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中终止。有一个问题:您的分叉计算是否会向父线程返回值?否,它们永远运行