【问题标题】:thread pool of Windows 10 has bugsWindows 10 的线程池有 bug
【发布时间】:2020-06-29 09:23:33
【问题描述】:

Why does Windows 10 start extra threads in my program?

让我们看一个简单的代码,

class AA
{
...
};

AA& getAA(){
  static AA aa;
  return aa;
}

class BB
{
  public:  BB() { getAA(); ... }
...
};
thread_local BB bb;

我添加了更多我遇到的信息。

。 用这个简单的代码,我重写了调用栈。

偶尔会在 0x00007FFB0B1272A6(ntdll.dll) 处引发异常。 访问冲突写入位置 0x000000024。 g_tss_mutex 的成员除 LockCount 为 6 和 LockSemaphore 为 0xffffffffffffffff 外,均为零。 该函数是 _Init_thread_lock() 并且它正在调用 EnterCriticalSection(&g_tss_mutex); 工作线程 ntdll.dll 线程抛出此异常。 调用堆栈是。

ntdll.dll!00007ffb0b1272a6()
ntdll.dll!00007ffb0b13b5f6()
ntdll.dll!00007ffb0b13b440()
_init_thread_lock() 
_Init_thread_header(int *pOnce)
getAA()
BB::BB()
`dynamic initializer for 'bb'()

主线程的调用栈是

_should_initialize_environment()
pre_c_initialization()
ucrtbased.dll!00007ffe9c1a4ab9
__scrt_common_main_seh()
__scrt_common_main()
WinMainCrtStartup()
....

Windows 10 线程池的Loader Threads 尝试初始化thread_local bb。但是如果加载器线程在进程的主线程不调用 _scrt_initialize_thread_safe_statics_platform_specific() 来调用 gerentee 静态魔法之前调用 bb 的构造函数。 https://docs.microsoft.com/en-us/cpp/build/reference/zc-threadsafeinit-thread-safe-local-static-initialization?view=vs-2019

我遇到了这种情况。我该怎么做才能解决这个问题。

【问题讨论】:

  • 我无法理解“但是如果加载程序线程...”这句话——你能澄清一下吗?
  • 对不起,我不明白这个问题。你能改写一下吗?
  • @tenfour LoaderThreads 表示ntdll为加载pararell库创建的线程。
  • @AsteroidsWithWings 抱歉。为了支持初始化静态局部变量的线程安全,调用了_scrt_initialize_thread_safe_statics_platform_specific()。因此,静态局部变量(getAA() 中的aa)的任何初始化都应该在该函数调用之后发生。但是,为了初始化 ntdll 的工作线程,在调用该函数之前调用 tls_init() 函数。此时,__bb 正在初始化,结果调用了“getAA()”中aa 的构造函数。
  • @walnut 对我的错误感到抱歉。我重写了代码。 __aa 应该是 __bb__bb 应该是 aa in getAA()

标签: c++ multithreading winapi threadpool ntdll


【解决方案1】:

即使 Windows 线程池不会产生自己的线程,您的进程中也可能始终有来宾。防病毒软件、管理软件、shell 扩展、辅助软件……甚至是恶意软件。

这里的关键问题是thread_local 早在线程启动时就被构造了。

您可以通过仅使用静态初始化使其构造函数 constexpr 来提供帮助,并在以后执行任何动态操作。

或者用std::optional 包装你的thread_local 对象,并在需要时自己构建它们。或者为此创建自己的包装器。

我知道这都是变通方法,解决方案是 MSVC 会延迟线程局部变量的构造。但我对解决方法感到满意。如果不是,请向 MS 发送反馈。

最终,通过延迟 thread_local 对象构造,您还可以优化:您不会为您的代码永远不会运行的线程构造对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-05
    • 2020-01-06
    • 2019-09-13
    • 1970-01-01
    • 2018-01-30
    • 2012-01-11
    • 2020-11-28
    • 1970-01-01
    相关资源
    最近更新 更多