可以在静态初始化期间安全地创建线程吗?

8
我记得曾经读过一篇文章,说在main()的第一行之前无法安全地创建线程,因为编译器会插入特殊代码以使线程工作正常,并且这些代码在静态初始化期间运行。因此,如果你有一个在构造函数中创建线程的全局对象,你的程序可能会崩溃。但现在我找不到原始文章了,我很想知道这个限制有多强 - 它是否严格符合标准?大多数编译器是否都是这样的?在C++0x中是否仍然如此?符合标准的编译器是否可以使静态初始化本身成为多线程的?(例如,检测到两个全局对象不相互影响,并在单独的线程上初始化它们以加速程序启动)。
编辑:为了澄清,我至少要感觉到实现在这方面是否真的存在显着差异,或者它是否是一种伪标准。例如,从技术上讲,标准允许重新排列属于不同访问说明符(public/protected等)的成员的布局。但我所知道的没有任何编译器这样做。

“按标准严格来说是这样的吗” - C++03标准对线程没有任何规定。就当前行为而言,可以查看POSIX(当然是一个标准,而不是标准),MSDN,Boost或其他编译器、平台和线程API的实现特定文档。 - Steve Jessop
3个回答

6
你所说的并不是语言本身,而是C运行时库(CRT)。首先,如果你在Windows上使用像CreateThread()这样的本地调用来创建线程,则可以在任何地方进行,因为它直接到达操作系统,没有CRT的干预。
你通常还有另一种选择,即使用CRT的_beginthread()。使用_beginthread()具有某些优点,例如具有线程安全的errno。阅读更多。如果你要使用_beginthread()创建线程,则可能会出现一些问题,因为_beginthread()所需的初始化可能未就位。
这涉及到一个更普遍的问题,即在 main()之前以及以何种顺序发生了什么。基本上,您有程序的入口点函数,它负责处理 main()之前需要发生的所有事情。使用Visual Studio,您实际上可以查看CRT中的此代码片段,并自行找出确切发生了什么。访问该代码的最简单方法是在代码中停止断点并查看 main()之前的堆栈帧。

谢谢,这让我对在Windows上使用MSVC的情况有了一些想法。不过我仍然很好奇其他平台的情况,而且它并没有真正回答在Windows上是否安全(_beginthread()是否实际依赖于可能尚未发生的任何初始化?)。 - Joseph Garvin
我也希望我知道这个。文档似乎没有提到它。 - shoosh

2

根本问题在于Windows对DllMain中可以和不可以执行的操作做了限制。特别地,你不能在DllMain中创建线程。静态初始化通常发生在DllMain中。因此,逻辑上讲,在静态初始化期间不能创建线程。


1
但请注意:在进程启动和DLL初始化例程期间,可以创建新线程,但它们只有在进程的DLL初始化完成后才开始执行。来源:http://msdn.microsoft.com/en-us/library/ms682453%28v=VS.85%29.aspx - Martin York
公正的观点,我没有看到那一点。请注意,该评论仅适用于CreateThread,而_beginthreadex没有此异常。 - MSalters

0
据我通过阅读C++0x/1x草案所了解的情况,启动一个线程在main()之前是可以的,但仍然受到静态初始化的常规陷阱的影响。符合规范的实现将不得不确保初始化线程的代码在任何静态或线程构造函数之前执行。

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