为什么 `std::exit` 没有按预期触发析构函数?
Why does `std::exit` not trigger destructors as expected?
#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
causes normal program termination to occur. Several cleanup steps are
performed:
The destructors of objects with thread local storage duration ... are
guaranteed to be called.
这里的问题是,当您退出进程时,线程将(在大多数现代多任务操作系统上)被强行杀死。这种线程的终止发生在 OS 级别,并且 OS 对对象或析构函数一无所知。
具有线程存储持续时间的对象保证只为调用exit
的线程销毁。引用 C++14 (N4140),[support.start.term] 18.5/8(强调我的):
[[noreturn]] void exit(int status)
The function exit()
has additional behavior in this International Standard:
- First, objects with thread storage duration and associated with the current thread are destroyed.
Next, objects with static storage duration are destroyed and functions registered by calling atexit
are called. See 3.6.3 for the order of destructions and calls. (Automatic objects are not
destroyed as a result of calling
exit()
.)
If control leaves a registered function called by exit
because the function does not provide a
handler for a thrown exception, std::terminate()
shall be called (15.5.1).
- Next, all open C streams (as mediated by the function signatures declared in
<cstdio>
) with
unwritten buffered data are flushed, all open C streams are closed, and all files created by calling
tmpfile()
are removed.
- Finally, control is returned to the host environment. If status is zero or
EXIT_SUCCESS
, an
implementation-defined form of the status successful termination is returned. If status is EXIT_FAILURE
, an implementation-defined form of the status unsuccessful termination is returned.
Otherwise the status returned is implementation-defined.
因此,该标准不保证销毁具有与调用 exit
.
以外的其他线程关联的线程存储持续时间的对象
#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
causes normal program termination to occur. Several cleanup steps are performed:The destructors of objects with thread local storage duration ... are guaranteed to be called.
这里的问题是,当您退出进程时,线程将(在大多数现代多任务操作系统上)被强行杀死。这种线程的终止发生在 OS 级别,并且 OS 对对象或析构函数一无所知。
具有线程存储持续时间的对象保证只为调用exit
的线程销毁。引用 C++14 (N4140),[support.start.term] 18.5/8(强调我的):
[[noreturn]] void exit(int status)
The function
exit()
has additional behavior in this International Standard:
- First, objects with thread storage duration and associated with the current thread are destroyed. Next, objects with static storage duration are destroyed and functions registered by calling atexit are called. See 3.6.3 for the order of destructions and calls. (Automatic objects are not destroyed as a result of calling
exit()
.) If control leaves a registered function called byexit
because the function does not provide a handler for a thrown exception,std::terminate()
shall be called (15.5.1).- Next, all open C streams (as mediated by the function signatures declared in
<cstdio>
) with unwritten buffered data are flushed, all open C streams are closed, and all files created by callingtmpfile()
are removed.- Finally, control is returned to the host environment. If status is zero or
EXIT_SUCCESS
, an implementation-defined form of the status successful termination is returned. If status isEXIT_FAILURE
, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.
因此,该标准不保证销毁具有与调用 exit
.