为什么Windows 10会在我的程序中启动额外的线程?

时间:2016-01-16 00:21:28

标签: c++ windows multithreading threadpool

使用Visual Studio 2015,在一个新的空C ++项目中,为控制台应用程序构建以下内容:

int main() {
    return 0;
}

在返回时设置断点并在调试器中启动程序。在Windows 7上,从断点开始,该程序只有一个线程。但在Windows 10上,它有五个(!)线程:主线程和四个“工作线程”等待同步对象。

谁在启动线程池(或者我如何找到)?

4 个答案:

答案 0 :(得分:25)

水晶球说调试> Windows> “线程”窗口在ntdll.dll!TppWorkerThread处显示这些线程。请务必启用Microsoft Symbol Server以自行查看,使用工具>选项>调试>符号。

这也发生在VS2013中,所以它绝对不是由新的VS2015诊断功能引起的,@ Adam的猜测是不正确的。

TppWorkerThread()是线程池线程的入口点。当我使用Debug>设置断点时新断点>关于此功能的功能断点。我很幸运在第二个线程池线程开始执行时捕获第一个线程池线程的堆栈跟踪:

    ntdll.dll!_NtOpenFile@24()  Unknown
    ntdll.dll!LdrpMapDllNtFileName()    Unknown
    ntdll.dll!LdrpMapDllSearchPath()    Unknown
    ntdll.dll!LdrpProcessWork() Unknown
    ntdll.dll!_LdrpWorkCallback@12()    Unknown
    ntdll.dll!TppWorkpExecuteCallback() Unknown
    ntdll.dll!TppWorkerThread() Unknown
    kernel32.dll!@BaseThreadInitThunk@12()  Unknown
    ntdll.dll!__RtlUserThreadStart()    Unknown
>   ntdll.dll!__RtlUserThreadStart@8()  Unknown

显然,加载程序正在使用Windows 10上的线程池来加载DLL。这肯定是新的:)此时主线程也在加载器中执行,并发工作。

因此,Windows 10正在利用多个内核来更快地初始化进程。非常一个功能,而不是一个bug :))

答案 1 :(得分:3)

这是默认的线程池。 https://docs.microsoft.com/en-us/windows/desktop/procthread/thread-pools

每个进程都有一个默认的线程池。

答案 2 :(得分:-1)

这也使我陷入困境,所以我决定找到我的个人答案;就像另一位海报所说的那样,它有点像“水晶球”,但是...

可能的原因是您的其中一个线程之一:

  • WaitForSingleObject或
  • WaitForMultipleObjects

在最新版本的Windows中执行此操作似乎会生成线程池,以方便等待对象(不知道为什么)。

这也可能是在您的主程序之前发生的,因为您有一些代码会导致创建一个全局范围的对象,然后在您甚至没有进入入口点之前就从代码开始(这甚至可能在Windows的某些标准库代码中) 10 SDK)。

对于任何想找出自己特定原因的人,您可以尝试以下方法:

class RunBeforeMain
{
public:
    RunBeforeMain()
    {
        HMODULE hNtDll = (HMODULE)LoadLibrary(_T("ntdll.dll"));
        FARPROC lpNeeded = GetProcAddress(hNtDll,"NtWaitForMultipleObjects");
        DebugBreakPoint();
    }
};

RunBeforeMain go;

int CALLBACK WinMain(
  _In_ HINSTANCE hInstance,
  _In_ HINSTANCE hPrevInstance,
  _In_ LPSTR     lpCmdLine,
  _In_ int       nCmdShow
)
{
}

运行此命令时,您将在lpNeeded中获取NtDll过程NtWaitForMultipleObjects的库加载位置,获取该地址并将其粘贴到反汇编视图窗口中,然后在第一行上放置一个断点。

现在继续运行您的解决方案。

警告:

  1. 我们无法有效控制全局变量的初始化顺序,这就是为什么如果您有良好的编码意识,便会不惜一切代价避免使用它们(除非有特殊需要)。由于这个事实,我们无法保证我们的全局变量将在其他全局变量导致其他线程之前触发。
  2. 虽然这是main之前的内容,但是任何库的DLL加载都将继续进行我们的任何调用,因此,可能已经为时已晚(您可以使用诸如强制不自动加载库之类的技巧,但这超出了我的意愿水平在这里大声笑)。

希望这对某人有帮助:)

答案 3 :(得分:-3)

我知道这是一个 5 年前的问题,但 Microsoft 已经告诉您它使用“遥测”,并且您已经知道 Microsoft Visual Studio 2015 及更高版本向您的可执行文件中注入了一些您不允许的额外代码编辑。根据微软的说法,额外的代码不是你的事,你无法控制它,也不允许你看到它。你懂的。您选择使用 Visual Studio,并且您知道 Microsoft 已经告诉您他们这样做并且您已经接受了后果。微软已经告诉你它会这样做。如果 Microsoft 注入的代码与您自己的代码冲突,那么不要期望您自己的代码能够按您的意愿优先运行。同样,您已经知道这一点。

那些额外的线程来自 Microsoft,而不是来自您,除非您选择使用 Visual Studio 2015 及更高版本。

不必惊讶。微软已经公开告诉你这一点。

[不是]等待你使用的线程池。 Microsoft 已经告诉您,他们在 Visual Studio 2015 及更高版本中执行此操作。

Windows 10 [未] 利用多核来更快地初始化进程。 [这是] [Microsoft 遥测] 的一个功能,而不是一个错误 [但实际上将 Microsoft 注入的遥测代码注入到您的可执行文件中,而您没有任何控制。] :[(].

想阻止吗?其中一种方法是: 在非遥测嵌入式操作系统或没有“遥测”功能的旧版 Microsoft 操作系统上编写 C++11 程序;无需任何版本的 Microsoft Visual Studio 即可编译;使用此程序启动和快速子类化您当前被 Microsoft 程序感染的程序,然后停止子类程序启动的任何线程,然后将所有请求重定向到您的子类程序最初不打算使用的 dll。

Microsoft 不是您的敌人,您选择做出然后忽略直至受伤的假设可能是您的敌人。