不为所有thread_local对象调用析构函数

时间:2017-01-11 13:24:42

标签: c++ multithreading gcc openmp

我在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

显然只会调用一个析构函数。

1 个答案:

答案 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。