了解线程化 GHC haskell 程序的子进程

Understanding the child process of a threaded GHC haskell program

我试图了解父线程和各种子 OS 线程如何在使用 GHC -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 测试的大型真实服务,例如这些 "my-service:w" 线程中的 14 个,在负载下这些进程 ID 似乎在搅动(其中一半在我终止服务之前保持活动状态)。

为什么是 14 个,为什么有一半生成并死亡?

我也会接受一个答案,该答案有助于指导我使用我的代码来解决后两个问题。

spawned at startup, it runs this function中的ghc_ticker。它的目的被描述为

The interval timer is used for profiling and for context switching in the threaded build.

其他 *:w 个线程是工作线程,它们是 created whenever there is more work to do (aka Task), but there are no more spare workers, see here

在启动时,ghc 为每个功能创建一个 worker,然后根据需要创建它们并在可能的情况下重复使用。很难说为什么 -N4 案例中有 14 个工人。我只能猜测它们正在为 IO 管理器线程提供服务:参见 here。我们也不要忘记 FFI——FFI 调用可能会阻塞 worker。你可以试试在createOSThread下个断点看看为什么要创建worker

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

添加: 嗯,我想我可以解释一下 N*2+1 工人:N 每个能力的工人是在启动时创建的; N 更多 - IO 管理器事件循环,每个功能一个;加上一个 IO 管理器计时器线程。虽然我不确定为什么第一个 N worker(在启动时创建)没有被 IO 管理器线程重用。