理解 GHC Haskell 程序中的子进程

6

我正在尝试理解使用-threaded编译的Haskell程序中,父线程和各个子线程是如何工作的。

使用:

module Main where
import Control.Concurrent
main = do
    threadDelay 9999999999

例如,在 ghc 8.6.5 上使用 -threaded 进行编译,并通过 +RTS -N3 运行时,我可以看到

$ pstree -p 6615 
hello(6615)─┬─{ghc_ticker}(6618)
            ├─{hello:w}(6616)
            ├─{hello:w}(6617)
            ├─{hello:w}(6619)
            ├─{hello:w}(6620)
            ├─{hello:w}(6621)
            ├─{hello:w}(6622)
            └─{hello:w}(6623)

看起来当我改变+RTS -N时,我会得到N*2 + 1个"hello:w"线程。 这些"hello:w"线程是什么,每个HEC + 1为什么会有两个线程? ghc_ticker是做什么的? 我还注意到,在我使用+RTS -N4的大型实际服务中,我会得到14个这样的"my-service:w"线程,并且在负载下,这些进程ID似乎会变动(其中一半保持活动状态,直到我杀死服务)。 为什么是14个,其中一半会生成和消失? 我也接受一个回答,帮助指导我如何对我的代码进行工具化,以解决后两个问题。

1
很确定那些是用于“安全”的 ffi 调用的线程池。 - Carl
1个回答

3

ghc_ticker 是在启动时 创建 的,它运行 这个函数。它的目的是:

间隔计时器用于剖析和在线程构建中进行上下文切换。

其他的 *:w 线程是工作线程,在有更多任务需要做(如Task)但没有多余的工作线程时被创建,参见 这里

在启动时,GHC 为每个能力创建一个工作线程,然后根据需要创建并尽可能重用它们。很难说为什么在 -N4 的情况下会有 14 个工作线程。我只能猜测它们正在服务于 IO 管理器线程:请参见 这里。不要忘记 FFI - FFI 调用可能会阻塞工作线程。您可以尝试在 createOSThread 中设置断点,以查看为什么会创建工作线程。

您可以在此处阅读有关调度程序的更多信息:here

附加:

嗯,我想我可以解释一下为什么会有 N*2+1 个工作线程:N 个工作线程在启动时就为每个能力创建了;另外的N个是IO管理器事件循环线程,每个能力一个;还有一个IO管理器定时器线程。虽然我不确定为什么第一个N个工作线程(在启动时创建的)没有被重用于IO管理器线程。


IO 管理器事件循环会在文件描述符上阻塞。这就是为什么它们必须在自己的线程上运行的原因。 - Andreas Klebinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接