Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 10 Threads
  gp 10.1 Unterschiede zwischen Threads und Prozessen
  gp 10.2 Thread-Bibliotheken
  gp 10.3 Kernel- und User-Threads
  gp 10.4 Scheduling und Zustände von Threads
  gp 10.5 Die grundlegenden Funktionen zur Thread–Programmierung
    gp 10.5.1 pthread_create – einen neuen Thread erzeugen
    gp 10.5.2 pthread_exit – einen Thread beenden
    gp 10.5.3 pthread_join – auf das Ende eines Threads warten
    gp 10.5.4 pthread_self – die ID von Threads ermitteln
    gp 10.5.5 pthread_equal – die ID von zwei Threads vergleichen
    gp 10.5.6 pthread_detach – einen Thread unabhängig machen
  gp 10.6 Die Attribute von Threads und das Scheduling
  gp 10.7 Threads synchronisieren
    gp 10.7.1 Mutexe
    gp 10.7.2 Condition-Variablen (Bedingungsvariablen)
    gp 10.7.3 Semaphore
    gp 10.7.4 Weitere Synchronisationstechniken im Überblick
  gp 10.8 Threads abbrechen (canceln)
  gp 10.9 Erzeugen von Thread-spezifischen Daten (TSD-Data)
  gp 10.10 pthread_once – Codeabschnitt einmal ausführen
  gp 10.11 Thread-safe (thread-sichere Funktionen)
  gp 10.12 Threads und Signale
  gp 10.13 Zusammenfassung und Ausblick


Rheinwerk Computing

10.8 Threads abbrechen (cancelntoptop

Wird ein Thread abgebrochen bzw. beendet, wurde bisher auch der komplette Thread beendet. Doch auch hierbei ist es möglich, auf eine Abbruchaufforderung zu reagieren. Hierzu sind drei Möglichkeiten vorhanden:

gp  PTHREAD_CANCEL_DISABLE – Damit legen Sie fest, dass ein Thread nicht abbrechbar ist. Dennoch bleiben Abbruchaufforderungen von anderen Threads nicht unbeachtet. Diese bleiben bestehen, und es kann ggf. darauf reagiert werden, wenn man den Thread wieder in einen abbrechbaren Zustand mittels PTHREAD_CANCEL_ENABLE setzt.
gp  PTHREAD_CANCEL_DEFERRED – Diese Abbruchmöglichkeit ist die Standardeinstellung bei den Threads. Bei einem Abbruch fährt der Thread so lange fort, bis der nächste Abbruchpunkt erreicht wurde. Man spricht von einem »verzögerten« Abbruchpunkt. Einen solchen »Abbruchpunkt« stellten u. a. Funktionen wie pthread_cond_wait(), pthread_cond_timewait(), pthread_join(), pthread_testcancel(), sem_wait(), sigwait(), open(), close(), read(), write() und noch viele weitere mehr da.
gp  PTHREAD_CANCEL_ASYNCHRONOUS – Hiermit wird der Thread gleich nach dem Eintreffen einer Abbruchaufforderung beendet. Hierbei handelt es sich um einen asynchronen Abbruch.

Hierzu die Funktionen, womit Sie einem anderen Thread einen Abbruch senden können und wie Sie die Abbruchmöglichkeiten selbst festlegen.

#include <pthread. h.>
int pthread_cancel( pthread_t thread );
int pthread_setcancelstate( int state, int *oldstate );
int pthread_setcanceltype( int type, int *oldtype );
void pthread_testcancel( void );

Mit der Funktion pthread_cancel() schicken Sie dem Thread mit der ID thread einen Abbruchaufforderung. Ob der Thread gleich abbricht oder erst beim nächsten Abbruchpunkt, hängt davon ab, ob hier PTHREAD_CANCEL_DEFERRED (Standard) oder PTHREAD_CANCEL_ASYNCHRONOUS verwendet wird. Bevor sich der Thread beendet, werden noch, falls verwendet, alle Exit-Handler-Funktionen ausgeführt.

Mit der Funktion pthread_setcancelstate() legen Sie fest, ob der Thread auf eine Abbruchaufforderung reagieren soll (PTHREAD_CANCEL_ENABLE = Default) oder nicht (PTHREAD_CANCEL_DISABLE). Im zweiten Parameter oldstate können Sie den zuvor eingestellten Wert für den Thread in der übergebenen Adresse sichern – oder, falls nicht benötigt, NULL angeben.

Die Funktion pthread_setcanceltype() hingegen legt über den Parameter type fest, ob der Thread verzögert (PTHREAD_CANCEL_DEFERRED = Default) oder asynchron (PTHREAD_CANCEL_ASYNCHRONOUS) beendet werden soll. Auch hier können Sie den alten Zustand des Threads in der Adresse oldtype sichern oder NULL verwenden.

Mit der Funktion pthread_testcancel() können Sie überprüfen, ob eine Abbruchaufforderung anliegt. Lag eine Abbruchbedingung vor, dann wird der Thread tatsächlich auch beendet. Sie können damit praktisch auch einen eigenen Abbruchpunkt festlegen.

Hier zunächst ein einfaches Beispiel zu pthread_cancel().

/* thread14.c */
#include <stdio.h>
#include <pthread. h.>
#include <stdlib.h>
#include <time.h>
pthread_t t1, t2, t3;
static int zufallszahl;
static void cancel_test1 (void) {
   if (zufallszahl > 25) {
      pthread_cancel (t3);
      printf ("(%d): Thread %ld beendet %ld\n",
         zufallszahl, pthread_self(), t3);
   }
   printf ("%ld zuende\n", pthread_self());
   pthread_exit ((void *) 0);
}
static void cancel_test2 (void) {
   if (zufallszahl <= 25) {
      pthread_cancel (t2);
      printf ("(%d): Thread %ld beendet %ld\n",
         zufallszahl, pthread_self(), t2);
   }
   printf ("%ld zuende\n", pthread_self());
   pthread_exit ((void *) 0);
}
static void zufall (void) {
   srand (time (NULL));
   zufallszahl = rand () % 50;
   pthread_exit (NULL);
}
int main (void) {
    if ((pthread_create (&t1, NULL, zufall, NULL)) != 0) {
       fprintf (stderr, "Fehler bei pthread_create ...\n");
       exit (EXIT_FAILURE);
    }
    if((pthread_create(&t2, NULL, cancel_test1, NULL))!=0) {
         fprintf (stderr, "Fehler bei pthread_create ...\n");
         exit (EXIT_FAILURE);
    }
    if((pthread_create(&t3, NULL, cancel_test2, NULL))!=0) {
         fprintf (stderr, "Fehler bei pthread_create ...\n");
         exit (EXIT_FAILURE);
   }
   pthread_join (t1, NULL);
   pthread_join (t2, NULL);
   pthread_join (t3, NULL);
   return EXIT_SUCCESS;
}

Hier werden drei Threads erzeugt. Einer der Threads erzeugt eine Zufallszahl, die anderen zwei Threads reagieren entsprechend auf diese Zufallszahl. Je nachdem, ob die Zufallszahl kleiner bzw. größer als 25 ist, beendet der eine Thread den anderen mit pthread_cancel(). Wenn Sie das Programm ausführen, wird trotzdem, nach Beendigung einer der beiden Threads mit pthread_cancel(), zweimal ausgegeben:

Thread n beendet 

Wie kann das sein, wo Sie doch mindestens einen Thread beendet haben? Das ist die zweite Bedingung zur Beendigung von Threads, nämlich die Reaktion auf die Abbruchanforderungen. Die Standardeinstellung lautet hier ja PTHREAD_CANCEL_DEFERRED. Damit läuft der Thread noch bis zum nächsten Abbruchpunkt, in unserem Fall pthread_exit(). Wenn Sie einen Thread sofort abbrechen wollen bzw. müssen, müssen Sie mit pthread_setcanceltype() die Konstante PTHREAD_CANCEL_ASYNCHRONOUS setzen, z. B. in der main-Funktion mit:

if ((pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,
                            NULL))!= 0) {
    fprintf(stderr, "Fehler bei pthread_setcanceltype\n");
    exit (EXIT_FAILURE);
}

In der Praxis kann man aber von asynchronen Abbrüchen abraten, da diese an jeder Stelle auftreten können. Wird z. B. pthread_mutex_lock() aufgerufen und tritt hier der Abbruch ein, nachdem das Mutex gesperrt wurde, hat man schnell einen Deadlock erzeugt. Einen asynchronen Abbruch sollte man in der Praxis nur verwenden, wenn die Funktion »asynchronsicher« ist, was mit pthread_cancel(), pthread_setcancelstate() und pthread_setcanceltype() nicht allzu viele Funktionen sind. Wenn Sie schon asynchrone Abbrüche verwenden müssen, dann eben immer wenn ein Thread keine wichtigen Ressourcen beinhaltet (wie reservierter Speicherplatz (Memory Leaks), Sperren etc.).

Ein besonders häufiger Anwendungsfall von PTHREAD_CANCEL_DISABLE sind kritische Codebereiche, die auf keinen Fall abgebrochen werden dürfen. Z. B. ist dies sinnvoll bei wichtigen Einträgen in Datenbanken, bei komplexen Maschinensteuerungen. Am besten realisiert man solche Codebereiche, indem man den kritischen Abschnitt als unabbrechbar einrichtet und gleich danach den alten Zustand wiederherstellt.

int oldstate;
/* Thread als unabbrechbar einrichten */
if ((pthread_setcancelstate( PTHREAD_CANCEL_DISABLE,
                             &oldstate))!= 0) {
    fprintf(stderr, "Fehler bei pthread_setcancelstate\n");
    exit (EXIT_FAILURE);
}
/* ----------------------------------------- */
/* Hier kommt der kritische Codebereich rein */
/* ----------------------------------------- */
/* Alten Zustand des Threads wieder herstellen */
if ((pthread_setcancelstate(oldstat, NULL))!= 0) {
    fprintf(stderr, "Fehler bei pthread_setcancelstate\n");
    exit (EXIT_FAILURE);
}

Ein einfaches Beispiel hierzu:

/* thread15.c */
#include <stdio.h>
#include <pthread. h.>
#include <stdlib.h>
#include <time.h>
static void cancel_test (void) {
   int oldstate;
   /* Thread als unabbrechbar einrichten */
   if ((pthread_setcancelstate( PTHREAD_CANCEL_DISABLE,
                                &oldstate))!= 0) {
       printf("Fehler bei pthread_setcancelstate\n");
       exit (EXIT_FAILURE);
   }
   printf("Thread %ld im kritischen Codeabschnitt\n",
      pthread_self());
   sleep(5); // 5 Sekunden warten
   /* Alten Zustand des Threads wieder herstellen */
   if ((pthread_setcancelstate(oldstate, NULL))!= 0) {
       printf("Fehler bei pthread_setcancelstate\n");
       exit (EXIT_FAILURE);
   }
   printf("Thread %ld nach dem kritischen Codeabschnitt\n",
      pthread_self());
   pthread_exit ((void *) 0);
}
int main (void) {
   pthread_t t1;
   int *abbruch;
   printf("Main-Thread %ld gestartet\n", pthread_self());
   if((pthread_create(&t1, NULL, cancel_test, NULL)) != 0) {
      fprintf (stderr, "Fehler bei pthread_create ...\n");
      exit (EXIT_FAILURE);
   }
   /* Abbruchaufforderung an den Thread */
   pthread_cancel(t1);
   pthread_join (t1, &abbruch);
   if( abbruch == PTHREAD_CANCELED )
      printf("Thread %ld wurde abgebrochen\n", t1);
   printf("Main-Thread %ld beendet\n", pthread_self());
   return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o thread15 thread15.c -lpthread
$ ./thread15
Main-Thread -1209416608 gestartet
Thread -1209418832 im kritischen Codeabschnitt
Thread -1209418832 nach dem kritischen Codeabschnitt
Thread -1209418832 wird abgebrochen
Main-Thread -1209416608 beendet

Ohne das Setzen von PTHREAD_CANCEL_DISABLE am Anfang des Threads »cancel_test« würde das Beispiel keine fünf Sekunden mehr warten und auch nicht mehr ausgeben »Thread –1209418832 nach dem kritischen Codeabschnitt« – am besten testen Sie dies, indem Sie das Verändern des Cancel-Status auskommentieren oder eben anstatt PTHREAD_CANCEL_DISABLE die Konstante PTHREAD_CANCEL_ENABLE verwenden.

 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern