提问人:Adrian McCarthy 提问时间:1/16/2016 更新时间:1/4/2023 访问量:13778
为什么 Windows 10 会在我的程序中启动额外的线程?
Why does Windows 10 start extra threads in my program?
问:
使用 Visual Studio 2015,在一个新的空 C++ 项目中,为控制台应用程序生成以下内容:
int main() {
return 0;
}
在返回时设置断点,并在调试器中启动程序。在 Windows 7 上,截至断点,此程序只有一个线程。但在 Windows 10 上,它有五个(!)线程:主线程和四个等待同步对象的“工作线程”。
谁在启动线程池(或者我如何找到)?
答:
Crystal ball 表示“调试”> Windows > Threads 窗口在 中显示这些线程。请务必启用 Microsoft 符号服务器 若要自行查看此内容,请使用“工具”>“选项”>“调试>符号”。ntdll.dll!TppWorkerThread
这也发生在 VS2013 中,因此它绝对不是由新的 VS2015 诊断功能引起的,@Adam猜测不可能是正确的。
TppWorkerThread() 是线程池线程的入口点。当我使用调试设置断点时> 新建断点>函数断点。当第二个线程池线程开始执行时,我很幸运地捕获了第一个线程池线程的堆栈跟踪:
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 正在利用多个内核来更快地初始化进程。很大程度上是一个功能,而不是一个错误:)
评论
这是默认的线程池。https://learn.microsoft.com/en-us/windows/desktop/procthread/thread-pools
每个进程都有一个默认线程池。
评论
这也引起了我的兴趣,所以我决定找到我个人的答案。正如另一张海报所说,这有点像“水晶球”的努力,但是......
可能的原因是您的线程之一,称为:
- 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 的库加载位置,获取该地址并将其粘贴到反汇编视图窗口中,然后在第一行放置一个断点。
现在继续运行解决方案。
几点注意事项:
- 我们无法有效地控制全局变量的初始化顺序,这就是为什么如果你有很好的编码能力,你会不惜一切代价避免它们(除非有一些特殊需要)。由于这个事实,我们不能保证我们的全局将在任何其他全局导致其他线程之前触发。
- 虽然这是在main之前,但任何库的DLL加载都会继续我们的任何调用,因此,可能已经太晚了(您可以使用诸如强制不自动加载库之类的技巧,但这远远超出了我在这里关心的意愿水平,哈哈)。
希望这对某人有所帮助:)
评论
CreateThread