为什么`std :: exit`不会按预期触发析构函数?

时间:2017-03-28 14:01:18

标签: c++ c++11 global-variables destructor thread-local-storage

#include <cstdlib>
#include <thread>
#include <chrono>
#include <iostream>

using namespace std;
using namespace std::literals;

struct A
{
    int n_ = 0;
    A(int n) : n_(n) { cout << "A:" << n_ << endl; }
    ~A() { cout << "~A:" << n_ << endl; }
};

A a1(1);

int main()
{
    std::thread([]()
    {
        static A a2(2);
        thread_local A a3(3);
        std::this_thread::sleep_for(24h);
    }).detach();

    static A a4(4);
    thread_local A a5(5);

    std::this_thread::sleep_for(1s);
    std::exit(0);
}

我的编译器为clang 5.0 -std=c++1z

输出如下:

A:1
A:2
A:4
A:5
A:3
~A:5
~A:2
~A:4
~A:1

请注意,没有~A:3,这意味着对象A a3未被破坏。

然而,根据cppref

  

std::exit会导致正常的程序终止。几个清理步骤是   进行:

     

具有线程本地存储持续时间的对象的析构函数...   保证被叫。

2 个答案:

答案 0 :(得分:37)

保证仅为调用exit的线程销毁具有线程存储持续时间的对象。引用C ++ 14(N4140),[support.start.term] 18.5 / 8(强调我的):

[[noreturn]] void exit(int status)
     

函数exit()在本国际标准中有其他行为:

     
      
  • 首先,销毁线程存储持续时间且与当前线程关联的对象。   接下来,通过调用atexit来销毁具有静态存储持续时间的对象并注册函数   叫做。有关破坏和调用的顺序,请参见3.6.3。 (自动对象不是   因调用exit()而被销毁。)   如果控制离开由exit调用的注册函数,因为该函数不提供   抛出异常的处理程序,std::terminate()将被调用(15.5.1)。
  •   
  • 接下来,所有打开的C流(由<cstdio>中声明的函数签名调解)   刷新未写入的缓冲数据,关闭所有打开的C流,并通过调用创建所有文件   tmpfile()已被移除。
  •   
  • 最后,控制权返回给主机环境。如果状态为零或EXIT_SUCCESS,则为   返回状态成功终止的实现定义形式。如果状态为EXIT_FAILURE,则返回状态为不成功终止的实现定义形式。   否则返回的状态是实现定义的。
  •   

因此,该标准不保证销毁与调用exit的线程之外的其他线程相关联的线程存储持续时间的对象。

答案 1 :(得分:14)

这里的问题是当你退出进程时,线程将(在大多数现代多任务操作系统上)被强行杀死。这种线程的杀死发生在操作系统级别,操作系统对对象或析构函数一无所知。