【问题标题】:Detached threads accessing global or static objects访问全局或静态对象的分离线程
【发布时间】:2019-06-15 07:31:37
【问题描述】:

以下文本摘自The C++ Standard Library: A Tutorial and Reference, 2nd Edition一书的第 18.2.1 节:: p>

但是请注意,生命周期问题也适用于全局和静态对象,因为当程序退出时,分离的线程可能仍然运行,这意味着它可能会访问已经销毁或正在销毁的全局或静态对象。不幸的是,这会导致未定义的行为。

据我了解,当main() 结束时,所有分离的线程都会终止。

因此,我怀疑这种行为的原因是全局和静态对象的实际销毁顺序对于分离线程的终止是未指定的,即它可能发生在之前,在分离的线程终止期间或之后。

我们将不胜感激。


更具体地说:在标题下的小节中注意分离的线程

【问题讨论】:

标签: c++ multithreading c++11 stdthread


【解决方案1】:

静态初始化或延迟初始化的所有内容(例如,输入了包含块的块范围静态变量)在正常程序终止期间被取消初始化 - 通过 main() 返回或在调用 @987654322 之后@。

问题不在于线程终止的顺序,而在于根本没有努力阻止它们。取而代之的是,将(可能仍在运行的)线程的收获委托给操作系统以在进程终止时进行排序。

实际上,对于一个实现来说,强制终止线程是非常困难的——不管是分离的还是其他的。除此之外,这是导致不可预知行为的秘诀,因为这些线程几乎总是被同步对象或系统调用阻塞,并持有资源(你好死锁!)。另一方面,Posix-Threads 没有提供这样的 API。线程需要从它们的线程函数返回以退出也就不足为奇了。

main() 返回和进程终止之间存在有限的时间段,在此期间运行时执行静态反初始化(与初始化的顺序严格相反)以及在atexit() 中注册的任何内容,期间任何现存的线程仍然可以运行。在大型程序中,这个时间可能很重要。

如果这些线程中的任何一个碰巧访问了一个静态初始化的对象,这当然是未定义的行为

我最近花了很多时间在一个包含大量 C++ 的大型 iOS 应用程序中跟踪一系列崩溃。

崩溃代码看起来很像这样,崩溃在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());
}


同时,在主线程上,调用了exit() 堆栈中的几个函数。

iOS 和 macOS 应用程序使用 Grand Central Dispatch/libdispatch 进行大量多线程处理,事实证明,在main() 退出后不仅线程仍在运行,而且还从后台调度队列执行作业。

我怀疑在许多其他系统上也会出现类似情况。

除了避免块作用域静态来支持不需要初始化的数据之外,我发现没有非常好的解决方案。

【讨论】:

  • 回复:“根本没有阻止他们的努力”——完全正确。 C++ 运行时会破坏静态和全局。它对分离的线程没有任何作用。
  • 我认为除非调用它们的析构函数(调用join()),否则您甚至无法保证附加线程将被终止。
  • ——如果一个线程没有加入也没有分离它的析构函数调用std::terminate
猜你喜欢
  • 2011-09-07
  • 2011-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 2011-02-26
相关资源
最近更新 更多