2014-07-02 5 views
6

Gemäß pthread_key_create Man-Seite können wir einen Destruktor zuordnen, der beim Herunterfahren des Threads aufgerufen werden soll. Mein Problem ist, dass die Destruktor-Funktion, die ich registriert habe, nicht aufgerufen wird. Giste meines Codes ist wie folgt.pthread_key_create destructor wird nicht aufgerufen

static pthread_key_t key; 
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT; 

void destructor(void *t) { 
    // thread local data structure clean up code here, which is not getting called 
} 

void create_key() { 
    pthread_key_create(&key, destructor); 
} 

// This will be called from every thread 
void set_thread_specific() { 

    ts = new ts_stack; // Thread local data structure 

    pthread_once(&tls_init_flag, create_key); 
    pthread_setspecific(key, ts); 
} 

Haben Sie eine Idee, was diesen Destruktor verhindern könnte? Ich benutze auch atexit() im Moment, um etwas im Hauptthread zu bereinigen. Gibt es eine Chance, dass die Destruktorfunktion gestört wird? Ich habe versucht, das auch zu entfernen. Trotzdem hat es nicht funktioniert. Auch mir ist nicht klar, ob ich den Hauptthread als separaten Fall mit atexit behandeln soll. (Es ist ein Muss, um die Art und Weise zu verwenden atexit, da ich an das Beenden der Anwendung einige anwendungsspezifische Bereinigung tun müssen,)

Antwort

0

Ich schrieb einen kurzen Test und das einzige, was ich geändert wurde Bewegen des create_key Anruf von Ihnen außerhalb der set_thread_specific .

Das heißt, ich nannte es innerhalb des Hauptthreads.

Ich sah dann meine Zerstörung erhalten aufgerufen, wenn die Thread-Routine beendet.

2

Dies ist von Entwurf.

Der Hauptthread wird beendet (durch Zurückgeben oder Aufrufen von exit()), und das verwendet pthread_exit() nicht. POSIX-Dokumente pthread_exit Aufruf der Thread-spezifischen Destruktoren.

Sie könnten pthread_exit() am Ende main hinzufügen. Alternativ können Sie atexit verwenden, um Ihre Zerstörung zu tun. In diesem Fall wäre es sauber, den threadspezifischen Wert auf NULL zu setzen, so dass für den Fall, dass die pthread_exit aufgerufen wurde, die Zerstörung nicht zweimal für diesen Schlüssel passieren würde.

UPDATE Eigentlich habe ich einfach, diese zu meinen globalen Unit-Test-Setup-Funktion meiner unmittelbaren Sorgen gelöst:

::atexit([] { ::pthread_exit(0); }); 

Also, in Zusammenhang mit meiner globalen Befestigung Klasse MyConfig:

struct MyConfig { 
    MyConfig() { 
     GOOGLE_PROTOBUF_VERIFY_VERSION; 
     ::atexit([] { ::pthread_exit(0); }); 
    } 
    ~MyConfig() { google::protobuf::ShutdownProtobufLibrary(); } 
}; 

Einige der verwendeten Referenzen:


PS. Natürlich C++ 11 introduced <thread> so haben Sie besser und mehr tragbare primitves mit zu arbeiten.

+0

Eine kurze Problemumgehung hinzugefügt, die ich derzeit nicht mit großen Nachteilen denken kann: ':: atexit ([] {:: pthread_exit (0);});' – sehe

+0

Beachten Sie, dass POSIX-Status * Die Funktionen von einem Anruf registriert to atexit() muss zurückkehren, um sicherzustellen, dass alle registrierten Funktionen * aufgerufen werden. Wenn Sie also 'pthread_exit()' in einer atexit-registrierten Funktion verwenden, können alle verbleibenden Funktionen auf dem atexit-Stack nicht aufgerufen werden. –

+0

@AlexanderKlauer sehr guter Punkt. Ich muss diesen Gedanken noch einmal untersuchen, wenn ich den Code erneut berühre – sehe

0

Ich nenne destructor() manuell am Ende main():

void * ThreadData = NULL; 

if ((ThreadData = pthread_getspecific(key)) != NULL) 
     destructor(ThreadData); 

Natürlich Schlüssel sollte richtig früher in main() Code initialisiert werden. PS. Pthread_Exit() aufrufen am Ende main() scheint gesamte Anwendung zu hängen ...

0

Ihr erster Gedanke an den Haupt-Thread als separater Fall mit atexit Handhabung funktionierte für mich am besten.

Achten Sie darauf, dass pthread_exit (0) den Exit-Wert des Prozesses überschreibt. Zum Beispiel wird das folgende Programm mit dem Status von Null sogar verlassen, obwohl main() kehrt mit Nummer drei:

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 

class ts_stack { 
public: 
    ts_stack() { 
    printf ("init\n"); 
    } 
    ~ts_stack() { 
    printf ("done\n"); 
    } 
}; 

static void cleanup (void); 

static pthread_key_t key; 
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT; 

void destructor(void *t) { 
    // thread local data structure clean up code here, which is not getting called 
    delete (ts_stack*) t; 
} 

void create_key() { 
    pthread_key_create(&key, destructor); 
    atexit(cleanup); 
} 

// This will be called from every thread 
void set_thread_specific() { 
    ts_stack *ts = new ts_stack(); // Thread local data structure 

    pthread_once(&tls_init_flag, create_key); 
    pthread_setspecific(key, ts); 
} 

static void cleanup (void) { 
    pthread_exit(0); // <-- Calls destructor but sets exit status to zero as a side effect! 
} 

int main (int argc, char *argv[]) { 
    set_thread_specific(); 
    return 3; // Attempt to exit with status of 3 
} 
0

Es ist schon in sehe Antwort, nur die wichtigsten Punkte in einer kompakten Art und Weise zu präsentieren:

  • pthread_key_create() Destruktoraufrufe werden durch einen Anruf an pthread_exit() ausgelöst.
  • Wenn die Startroutine eines Threads zurückgegeben wird, verhält sich das Verhalten so, als ob pthread_exit() aufgerufen wurde (d. H., Destruktoraufrufe werden ausgelöst).
  • Allerdings, wenn main() zurückgibt, ist das Verhalten als ob exit() aufgerufen wurde - keine Destruktor Aufrufe ausgelöst werden.

Dies wird in http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html erklärt. Siehe auch C++ 17 6.6.1p5 oder C11 5.1.2.2.3p1.

Verwandte Themen