2017-01-11 3 views
2

Ich arbeite mit OpenMP unter GCC 6.2.0 und C++ 1z. Ich habe versucht, thread_local Objekte zu verwenden, die in Threads erstellt werden, wenn sie benötigt werden. thread_local Objekte funktionieren fast gut, aber es scheint, dass Destruktoren nur für einen Thread aufgerufen werden. Ich kann das Problem mit dem folgenden Code simulieren. Verwendet der Code ein nicht zugelassenes Feature oder besteht möglicherweise ein Problem mit der GCC-Implementierung?Destruktor wird nicht für alle thread_local Objekte aufgerufen

#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; 
} 

Code ist kompiliert und unter Linux mit 4-Core-i7-CPU bauen. Befehle für die Erstellung sehen so aus:

$ 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 

Ausgabe des Programms sieht so aus:

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 

Offensichtlich nur eine destructor genannt wird.

+1

Wie viele Konstruktoren werden aufgerufen? –

+1

Thread lokalen und Thread-Pools ist eine todsichere Möglichkeit, Speicherlecks und andere Spaß Probleme zu erstellen. – Voo

+0

OpenMP hat seine eigene Möglichkeit, threadlokale Variablen mit '#pragma omp threadlocal (...)' zu deklarieren. Es ist oft (aber nicht immer) implementierungskompatibel mit den Sprachkonstrukten, z. B. wird derselbe TLS-Mechanismus verwendet, aber die Semantik unterscheidet sich. Mischen Sie nicht OpenMP und C++ - Threading! –

Antwort

0

Das Mischen von C++ - Sprach-Threading-Features und OpenMP ist nicht gut definiert. (Siehe relatedquestions). Grundsätzlich bezieht sich OpenMP nur auf C++ 98, daher ist die Interaktion mit OpenMP und threadlocal nicht sicher/portabel. Es wird normalerweise angenommen, dass es funktionieren wird, weil Implementierungen das Richtige tun, aber in diesem Fall scheinen sie es nicht zu tun. BTW: Ich kann das gleiche Problem mit Intel Compiler/OpenMP Runtime reproduzieren.

Der sichere und portable Ansatz besteht darin, entweder an reinem C++ 17 oder OpenMP zu bleiben. Mit OpenMP bedeutet dies ptr als privat zu definieren:

static std::unique_ptr<X> ptr; 
#pragma omp parallel 
{ 
    ptr.reset(); 
    #pragma omp for 
    for (unsigned x = 0; x < 32; ++x) { 

Beachten Sie, dass die reset notwendig ist, andernfalls wird der Wert von ptr nicht definiert sind. Sie können nicht verwenden firstprivate als std::unique_ptr hat keine Kopie-Ctor.

Verwandte Themen