代码之家  ›  专栏  ›  技术社区  ›  jfMR

访问全局或静态对象的分离线程

  •  2
  • jfMR  · 技术社区  · 6 年前

    以下文字摘自第18.2.1节。 阿西 这本书的标题是 C++标准库:教程和参考文献,第二版 :

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

    据我所知,所有分离的线程将在 main() 末端。

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

    如对此问题作进一步澄清,将不胜感激。


    阿西 更具体一点:在标题下的小节中 当心断开的线 .

    1 回复  |  直到 6 年前
        1
  •  2
  •   marko    6 年前

    在正常程序终止期间,静态初始化或延迟初始化的所有内容(例如,输入了包含块的块作用域静态变量)都会被取消初始化-或者 main() 返回或跟踪呼叫 exit() .

    问题不在于线程终止的顺序,而是 根本没办法阻止他们 . 相反,当进程终止时,将获取(可能仍在运行)线程委托给操作系统进行排序。

    实际上,对于一个实现来说,强制终止线程是非常困难的——分离或其他方式。除了其他事情之外,这是一个不可预知行为的秘诀,因为这些线程几乎总是被同步对象或系统调用阻塞,并保留资源(hello deadlocks!)另一方面,POSIX线程不提供这样做的API。线程需要从其线程函数返回以退出并不奇怪。

    两者之间有有限的时间间隔 主体() 返回并终止运行时执行静态取消初始化(严格按照与初始化相反的顺序)以及在其中注册的任何内容的进程。 atexit() ,在此期间任何现有线程仍可以运行。在一个大的程序中,这一次可能很重要。

    如果这些线程中的任何一个恰好访问静态初始化的对象,那么这当然是 未定义行为 .

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

    崩溃的代码看起来很像这样,崩溃在 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进行多线程处理,结果不仅是在 主体() 退出,但作业也正在后台调度队列中执行。

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

    除了避免使用块范围静态数据来处理不需要初始化的数据之外,我没有找到一个非常好的解决方案。