以下文字摘录自标题为 C ++标准库:教程和参考,第二版的书的18.2.1 †部分:>
但是,请注意,生存期问题也适用于全局和静态对象,因为当程序退出时,分离的线程可能仍在运行,这意味着它可能会访问已被破坏或正在破坏的全局或静态对象。不幸的是,这将导致不确定的行为。
据我了解,所有分离的线程将在main()
结束时终止。
因此,我怀疑此行为的原因是,对于分离线程的终止,全局和静态对象的实际销毁顺序是未指定,即,它可能发生在之前,在分离的线程终止期间或之后。
对此事的任何进一步澄清将不胜感激。
† 要更具体:在小节标题为当心分离的线程。
答案 0 :(得分:2)
在正常程序终止期间,所有静态初始化或延迟初始化的东西(例如,输入了包含块的块范围静态变量)都会被取消初始化-通过main()
返回或在调用之后exit()
。
问题不在于线程终止的顺序,而在于根本没有任何努力来终止线程。取而代之的是,将(可能仍在运行的)线程的收获委托给操作系统,以在进程终止时进行整理。
实际上,对于实现强制终止线程(分离的线程或其他线程),真的很困难。除了别的什么,这是导致无法预料的行为的秘诀,因为这些线程几乎总是在同步对象或系统调用上被阻塞,并拥有资源(您好死锁!)。另外,Posix-Threads不提供这样做的API。线程需要从其线程函数返回以退出,也就不足为奇了。
从main()
返回到过程终止之间有一段有限的时间,在该过程中,运行时将执行静态反初始化(与初始化的顺序严格相反)以及在{{1 }},在此期间任何现有线程仍可以运行。在大型程序中,这段时间可能很重要。
如果这些线程中的任何一个碰巧访问了静态初始化的对象,那么这当然是未定义的行为。
最近,我花了很多时间来追踪具有大量C ++的大型iOS应用程序中的一系列崩溃。
崩溃代码看起来很像,崩溃深陷于atexit()
std::set<T>::find(const T&)
同时,在主线程上,调用了堆栈中的
bool checkWord(const std::string &w)
{
static std::set<std::string> tags{"foo", "bar"};
return (tags.find(w) != tags.end());
}
几个函数。
iOS和macOS应用程序使用Grand Central Dispatch / libdispatch进行了多线程处理,事实证明,不仅exit()
退出后线程仍在运行,而且作业也在后台调度队列中执行。 / p>
我怀疑在许多其他系统上也会有类似情况。
除了避免使用块范围内的静态数据支持不需要初始化的数据外,我没有找到解决该问题的绝佳方法。