C++ 析构函数是';t调用所有线程\u本地对象
我在GCC 6.2.0和C++1z下使用OpenMP。我尝试使用C++ 析构函数是';t调用所有线程\u本地对象,c++,multithreading,gcc,openmp,C++,Multithreading,Gcc,Openmp,我在GCC 6.2.0和C++1z下使用OpenMP。我尝试使用thread\u local对象,这些对象在需要时在线程内部创建thread\u local对象几乎可以正常工作,但似乎只为一个线程调用析构函数。我可以用下面的代码模拟这个问题。代码是否使用了一些不允许的特性,或者GCC实现是否存在一些问题 #include <iostream> #include <memory> #include <mutex> #include <thread>
thread\u local
对象,这些对象在需要时在线程内部创建thread\u local
对象几乎可以正常工作,但似乎只为一个线程调用析构函数。我可以用下面的代码模拟这个问题。代码是否使用了一些不允许的特性,或者GCC实现是否存在一些问题
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <sstream>
std::mutex g_cerr_mutex;
struct X {
std::string name_;
X() {
std::stringstream ss;
ss << std::this_thread::get_id();
name_ = ss.str();
}
~X() noexcept {
std::lock_guard<std::mutex> guard(g_cerr_mutex);
std::cerr << "Destructing: " << name_ << std::endl;
}
};
int main(void) {
static thread_local std::unique_ptr<X> ptr;
#pragma omp parallel for
for (unsigned x = 0; x < 32; ++x) {
if (!ptr) {
ptr.reset(new X);
}
std::lock_guard<std::mutex> guard(g_cerr_mutex);
std::cerr << std::this_thread::get_id() << " : " << static_cast<void*>(ptr.get()) << std::endl;
}
return 0;
}
程序的输出如下所示:
$ g++ -std=gnu++1z -fopenmp -Wall -Werror -Ofast -pthread -c omp.cpp
$ g++ -std=gnu++1z -fopenmp -Wall -Werror -Ofast -pthread omp.o -o omp
139868398491392 : 0x7f35780008c0
139868398491392 : 0x7f35780008c0
139868398491392 : 0x7f35780008c0
139868398491392 : 0x7f35780008c0
139868453738496 : 0x7bc2d0
139868453738496 : 0x7bc2d0
139868453738496 : 0x7bc2d0
139868453738496 : 0x7bc2d0
139868423669504 : 0x7f35880008c0
139868423669504 : 0x7f35880008c0
139868423669504 : 0x7f35880008c0
139868423669504 : 0x7f35880008c0
139868406884096 : 0x7f35700008c0
139868406884096 : 0x7f35700008c0
139868406884096 : 0x7f35700008c0
139868406884096 : 0x7f35700008c0
139868432062208 : 0x7f35a00008c0
139868432062208 : 0x7f35a00008c0
139868432062208 : 0x7f35a00008c0
139868432062208 : 0x7f35a00008c0
139868390098688 : 0x7f35900008c0
139868390098688 : 0x7f35900008c0
139868390098688 : 0x7f35900008c0
139868390098688 : 0x7f35900008c0
139868415276800 : 0x7f35980008c0
139868415276800 : 0x7f35980008c0
139868415276800 : 0x7f35980008c0
139868415276800 : 0x7f35980008c0
139868381705984 : 0x7f35800008c0
139868381705984 : 0x7f35800008c0
139868381705984 : 0x7f35800008c0
139868381705984 : 0x7f35800008c0
Destructing: 139868453738496
显然只有一个析构函数。< /P> < P>混合C++语言线程特性和OpenMP没有定义好。(见附件)。基本上OpenMP只引用C++98,因此与OpenMP和
threadlocal
的交互不安全/不可移植。通常认为它会起作用,因为实现做了正确的事情,但在这种情况下,显然不会。顺便说一句:我可以用英特尔编译器/OpenMP运行时重现同样的问题
安全且可移植的方法是坚持使用纯C++17或OpenMP。使用OpenMP,这意味着将ptr
定义为私有:
static std::unique_ptr<X> ptr;
#pragma omp parallel
{
ptr.reset();
#pragma omp for
for (unsigned x = 0; x < 32; ++x) {
static std::unique_ptr ptr;
#pragma-omp并行
{
ptr.reset();
#pragma omp for
用于(无符号x=0;x<32;++x){
请注意,
reset
是必需的,否则ptr
的值是未定义的。您不能使用firstprivate
,因为std::unique\u ptr
没有复制构造函数。调用了多少构造函数?线程本地池和线程池肯定会造成内存泄漏和其他有趣的问题。OpenMP有自己的mea使用#pragma omp threadlocal(…)
声明线程局部变量的数量。通常(但不总是!)与语言构造兼容的实现,例如使用相同的TLS机制,但语义不同。不要混合OpenMP和C++线程。BTW,您是否尝试用“代码> STD::线程< /代码>运行相同的代码?根本不调用析构函数。在OpenMP情况下,线程0是主线程,因此您得到了析构函数。GCC使用ELF TLS实现线程局部变量,就我所知,它没有(或者至少过去没有)支持构造函数和析构函数,因此GCC只允许POD作为线程局部变量。这在GCC的OpenMPthreadprivate
中公开-如果您尝试类似于#pragma omp threadprivate(ptr)的操作
而不是thread\u local
,GCC抛出一个错误。Hristo,你是对的。对于std::thread,它也不调用析构函数,只调用构造函数。。。