一位同事写道:
namespace {
thread_local constinit int lastY = -1;
thread_local std::unique_ptr<AvxBezierAndSaturation> pAvxBezierAndSaturation{};
}
void Class::functionName(....)
{
:
if (!static_cast<bool>(pAvxBezierAndSaturation))
pAvxBezierAndSaturation = std::make_unique<AvxBezierAndSaturation>(bufferLen);
}
当程序终止 AvxBezierAndSaturation 的所有实例时,VLD 会将其分配的存储报告为存储泄漏。
这是 thread_local unique_ptr 的正确用法吗?如果不是,应该如何使用?
谢谢 大卫
最可能的问题可能是程序退出时并非所有线程都已终止。
鉴于您正在使用 openmp,这些很可能属于 openmp。
标准中有两种情况可以结束
thread_local
变量的生命周期:
std::exit()
被称为thread_local
的线程的所有 std::exit
变量。std::exit
的线程之外可能仍在运行的任何其他线程。main()
回来会为您打电话std::exit()
。这包括:
6.9.3.4 程序执行 - 启动和终止 [basic.start.term] [强调我的]
(2) 给定线程内具有线程存储持续时间的构造对象将由于 从该线程的初始函数返回以及由于 该线程调用
而被销毁。在销毁任何具有静态存储持续时间的对象之前,该线程内所有具有线程存储持续时间的构造对象的销毁都会发生。std::exit
17.5 启动和终止 [support.start.term] [强调我的]
[[noreturn]] void exit(int status);
(9) 效果:
(9.1) - 首先,具有线程存储持续时间且与当前线程关联的对象被销毁。接下来,具有静态存储持续时间的对象被销毁,并调用通过调用atexit注册的函数。 [...]
因此,只要确保在调用
std::exit
时没有其他线程正在运行(通过从 main()
返回隐式或显式),那么所有 thread_local
变量都应该被正确清理。
假设您正在使用 msvc 和 openmp,因为您正在使用 vld(并在评论中提到了 openmp)。
OpenMP 不是 C++ 标准的一部分,因此规则可能略有不同。
MSVC 的 OpenMP 文档包含以下段落:
(注意openmp
threadprivate
、__declspec(thread)
和thread_local
在msvc中都是一样的)
OpenMP 库参考 - 指令 - threadprivate
线程私有
备注
[...]
不保证可破坏类型的 threadprivate 变量会调用其析构函数。
[...]
用户无法控制构成并行区域的线程何时终止。如果进程退出时这些线程存在,则线程不会收到有关进程退出的通知,并且除了退出的线程(此处为主线程)之外的任何线程上都不会为 threaded_var 调用析构函数。因此,代码不应依赖于正确销毁 threadprivate 变量。
即如果您使用 openmp,则不能保证会调用
thread_local
变量的析构函数。