我在GCC 6.2.0和C ++ 1z下使用OpenMP。我尝试使用在需要时在线程内创建的thread_local
个对象。 thread_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;
}
使用4核i7 CPU在Linux下编译和构建代码。编译命令看起来像这样:
$ 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
显然只会调用一个析构函数。
答案 0 :(得分:0)
混合C ++语言线程功能和OpenMP没有明确定义。 (见related questions)。基本上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) {
请注意,reset
是必需的,否则ptr
的值未定义。您无法使用firstprivate
,因为std::unique_ptr
没有copy-ctor。