pthread_exit vs. return

Lesezeit: 6 Minuten

Ich habe eine verbindbare Pthread-Runner-Funktion, die wie folgt definiert ist:

void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}

Dieser Thread soll dem Hauptthread beitreten.

Immer wenn ich mein Programm durch Valgrind laufen ließ, bekam ich das nach Leaks:

LEAK SUMMARY:
   definitely lost: 0 bytes in 0 blocks
   indirectly lost: 0 bytes in 0 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 968 bytes in 5 blocks
        suppressed: 0 bytes in 0 blocks

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10)

Ich habe die Manpage auf pthreads überprüft, die besagten:

  The new thread terminates in one of the following ways:

   * It  calls  pthread_exit(3),  specifying  an exit status value that is
     available  to  another  thread  in  the  same  process   that   calls
     pthread_join(3).

   * It  returns  from  start_routine().   This  is  equivalent to calling
     pthread_exit(3) with the value supplied in the return statement.

   * It is canceled (see pthread_cancel(3)).

   * Any of the threads in the process calls exit(3), or the  main  thread
     performs  a  return  from main().  This causes the termination of all
     threads in the process.

Als ich pthread_exit() durch eine return-Anweisung ersetzt habe, die Lecks verschwanden.

return(NULL);

Meine eigentliche Frage ist dreigleisig:

  1. Kann jemand erklären, warum die Rücksendeerklärung keine Lecks ergab?
  2. Gibt es einen grundlegenden Unterschied zwischen beiden Aussagen in Bezug auf das Verlassen von Threads?
  3. Wenn ja, wann sollte das eine dem anderen vorgezogen werden?

  • Benutzt du wirklich C++? C++ verwendet den Bereich, um Objekte zu zerstören, und die Rückgabe würde diesen Bereich “verlassen”, während pthread_exit dies nicht tut.

    Roger Pate

    2. Oktober 2010 um 6:47 Uhr

  • Es tut mir leid, aber ich erwähne C++ nirgendwo in meiner Frage. Ich mache jetzt alles in C.

    Benutzer191776

    2. Oktober 2010 um 6:55 Uhr

  • Ich weiß, du hast es nicht erwähnt, aber es war eine Vermutung, deshalb habe ich gefragt. 🙂 Könnten Sie eine vollständige bereitstellen Prüfung Fall?

    Roger Pate

    2. Oktober 2010 um 7:02 Uhr


Der folgende minimale Testfall zeigt das von Ihnen beschriebene Verhalten:

#include <pthread.h>
#include <unistd.h>

void *app1(void *x)
{
    sleep(1);
    pthread_exit(0);
}

int main()
{
    pthread_t t1;

    pthread_create(&t1, NULL, app1, NULL);
    pthread_join(t1, NULL);

    return 0;
}

valgrind --leak-check=full --show-reachable=yes zeigt 5 Blöcke, die von Funktionen zugeordnet sind, die von aufgerufen werden pthread_exit() das ist nicht freigegeben, aber am Prozessende noch erreichbar. Wenn die pthread_exit(0); wird ersetzt durch return 0;die 5 Blöcke sind nicht belegt.

Wenn Sie jedoch das Erstellen und Verbinden einer großen Anzahl von Threads testen, werden Sie feststellen, dass die Menge an nicht freigegebenem Speicher, die beim Beenden verwendet wird, dies tut nicht Zunahme. Dies und die Tatsache, dass es immer noch erreichbar ist, weist darauf hin, dass Sie nur eine Kuriosität der glibc-Implementierung sehen. Mehrere glibc-Funktionen weisen Speicher mit zu malloc() das erste Mal, wenn sie aufgerufen werden, die ihnen für den Rest der Prozesslebensdauer zugewiesen bleiben. glibc macht sich nicht die Mühe, diesen Speicher beim Beenden des Prozesses freizugeben, da es weiß, dass der Prozess ohnehin abgebaut wird – es wäre nur eine Verschwendung von CPU-Zyklen.

  • Gibt es keine Möglichkeit, das Freigeben von Speicher zu erzwingen, der von glibc zugewiesen wurde? Nicht, dass es notwendig wäre, aber ich glaube, ich habe es irgendwo auf dieser Seite erwähnt gesehen …

    – Christoffer

    2. Oktober 2010 um 7:37 Uhr


  • @Christoffer: Valgrind ruft seit ungefähr Version 1.1 oder so eine Funktion namens auf __libc_freeres der das machen soll. Es gibt jedoch einige Versionen von glibc, die Fehler in dieser Funktion haben (da sie im Allgemeinen nicht aufgerufen wird, es sei denn, das Programm läuft unter einem Speicher-Debugger). Es wird normalerweise standardmäßig aufgerufen, es sei denn, valgrind wird mit dem Argument ausgeführt --run-libc-freeres=no.

    – Doug

    2. Oktober 2010 um 8:10 Uhr


  • Vielen Dank! Das machte mich wahnsinnig. Ich dachte, es wäre eine oberflächliche Kuriosität, aber ich war mir nicht sicher.

    – Wilhelm

    23. April 2012 um 6:03 Uhr

Benutzeravatar von Steven S
Steven S

Ich bin mir nicht sicher, ob Sie noch daran interessiert sind, aber ich debugge derzeit eine ähnliche Situation. Threads, die verwenden pthread_exit Valgrind veranlassen, erreichbare Blöcke zu melden. Der Grund scheint hier ziemlich gut erklärt zu sein:

https://bugzilla.redhat.com/show_bug.cgi?id=483821

Im Wesentlichen scheint es pthread_exit verursacht a dlopen die niemals explizit bereinigt wird, wenn der Prozess beendet wird.

Benutzt du zufällig C++? Zur Verdeutlichung – Ihre Quelldatei endet mit a .c Erweiterung, und Sie kompilieren es mit gccnicht g++?

Es scheint ziemlich wahrscheinlich, dass Ihre Funktion Ressourcen zuweist, von denen Sie erwarten, dass sie automatisch bereinigt werden, wenn die Funktion zurückkehrt. Lokale C++-Objekte wie std::vector oder std::string Wenn Sie dies tun, werden ihre Destruktoren wahrscheinlich nicht ausgeführt, wenn Sie aufrufen pthread_exitwürde aber aufgeräumt werden, wenn Sie einfach zurückkehren.

Meine Präferenz ist es, Low-Level-APIs wie zu vermeiden pthread_exit, und kehren Sie nach Möglichkeit immer nur von der Thread-Funktion zurück. Sie sind gleichwertig, abgesehen davon pthread_exit ist ein De-facto-Flusssteuerungskonstrukt, das die von Ihnen verwendete Sprache umgeht, aber return nicht.

  • Und Sie kompilieren mit gccund es besteht keine Möglichkeit, dass es oder irgendein Code, den es aufruft, versehentlich C++-Features verwendet?

    – Doug

    2. Oktober 2010 um 6:57 Uhr

  • Seien Sie versichert, ich kompiliere mit gcc. Bitte schauen Sie sich die Tags an, bevor Sie antworten.

    Benutzer191776

    2. Oktober 2010 um 6:58 Uhr


  • @crypto: Ich schaue mir die Tags an. Es ist nicht immer klar, ob Leute einen reinen C-Compiler verwenden oder ob sie eine C-ähnliche Teilmenge von C++ verwenden. In jedem Fall ist die Antwort von caf wahrscheinlich relevanter.

    – Doug

    2. Oktober 2010 um 7:09 Uhr

Benutzeravatar von Dustin Oprea
Dustin Oprea

Es sieht so aus, als würde der Aufruf von exit() (und anscheinend pthread_exit()) automatisch zugewiesene Variablen zugewiesen lassen. Sie müssen entweder zurückkehren oder werfen, um sich richtig zu entspannen.

Per C++ Valgrind mögliche Lecks in der STL-Zeichenfolge:

@ Anspruch: Ich sehe nicht, wo dieses Dokument sagt, dass ich falsch liege, aber wenn ja, dann ist es falsch. Um den C++-Standard (§18.3/8) zu zitieren: „Automatische Objekte werden als Ergebnis des Aufrufs von exit() nicht zerstört.“ – James McNellis 10. September 10 um 19:11 Uhr

Da das Ausführen von “return 0” anstelle von “pthread_exit(0)” Ihr Problem zu lösen schien (und meins … danke), gehe ich davon aus, dass das Verhalten zwischen den beiden ähnlich ist.

Ich habe die Erfahrung gemacht, dass Valgrind Schwierigkeiten hat, den Speicher zu verfolgen, der für den Status von verbindbaren Threads zugewiesen wird. (Dies geht in die gleiche Richtung wie caf andeutet.)

Da es scheint, dass Sie immer einen Wert von zurückgeben 0 Ich vermute, dass Sie Ihre Threads vielleicht aus Anwendungssicht zusammenführen müssen? Wenn dies der Fall ist, erwägen Sie, sie von Anfang an losgelöst zu starten, wodurch die Zuweisung dieses Speichers vermieden wird.

Der Nachteil ist, dass Sie entweder:

  1. um Ihre eigene Barriere am Ende Ihres zu implementieren main. Wenn Sie die Anzahl der Threads vorher kennen, wird eine einfache statisch zugewiesen
    pthread_barrier würdest du.
  2. oder dich zu verlassen main mit
    pthread_exit so dass Sie den Rest der laufenden Threads, die möglicherweise noch nicht beendet sind, nicht beenden.

Benutzeravatar von rajan goel
Rajan Goel

es ist schwierig für Valgrind, pthread_exit-Variablen zu verfolgen, deshalb zeigt es mit pthread_exit ein Speicherleck mit noch erreichbarem Tag; aber nicht im Fall mit Rückgabe.

1403100cookie-checkpthread_exit vs. return

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy