Galileo Computing < openbook >
Galileo Computing - Bücher zur Programmierung und Softwareentwicklung
Galileo Computing - Bücher zur Programmierung und Softwareentwicklung


'Wie werde ich Unix-Guru' als Buch bestellen
A. Willemer
Wie werde ich UNIX-Guru
I  ANWENDUNG
Know-How für Unix/Linux-User: Einführung, Shell, Befehle, Hilfe, Arbeit mit Dateien, Editoren, Reguläre Ausdrücke, nützliche Tools, Hardware.

II  ADMINISTRATION
Tools, Systemstart, Benutzer verwalten, Hardware konfigurieren, Software installieren, Datensicherung, Tuning, Kernel

III  NETZWERK
Client/Server Systeme, TCP/IP, Routing, IPv6, Internet-Dienste, DHCP, Webserver, Firewalls

IV  DAS X-WINDOW SYSTEM
Die grafische Oberfläche von UNIX einrichten und nutzen

V  PROGRAMMIERUNG VON SHELLSKRIPTEN
Automatisieren von Tasks durch Shell-Skripte.

VI  PERL
Interpreter, Syntax, Variablen, Steuerung, Funktionen, UNIX-Aufrufe, GUIs mit Tk

VII  PROGRAMMIERWERKZEUGE
C-Compiler, Analyse-Tools, CVS, yacc, diff

VIII  UNIX-SYSTEMAUFRUFE
UNIX-Befehle in eigenen Programmen nutzen

IX  LITERATUR
Weiterführende Literatur zu UNIX und LINUX

 
Galileo Computing / <openbook> / "Wie werde ich UNIX-Guru ?"
« Message Queues Prozesse Signale »

Leichtgewichtsprozesse: Threads

Relativ neu ist die Möglichkeit, unter UNIX mit Threads zu arbeiten. Es gab zunächst unterschiedliche Herstellerstandards für die Programmierschnittstelle von Sun, Unisys und SGI. Linux definierte einfach Prozesse, die sich Ressourcen teilten und erzeugte diese Quasi-Threads mit dem Aufruf clone(). Inzwischen gibt es einen Standard nach POSIX 1003.1c, der sich mit der Zeit sicher durchsetzen wird und dafür sorgt, dass Threads auf allen UNIX-Systemen gleich programmiert werden.

Threads werden dort eingesetzt, wo parallele Vorgänge benötigt werden, bei denen möglichst wenig Synchronisation erforderlich ist oder in Situationen in denen die parallelen Vorgänge sich viele Ressourcen teilen müssen. Im Gegensatz zu Prozessen teilen sich Threads alle Ressourcen bis auf den Programmzeiger. Das bedeutet, dass die Änderung einer globalen Variablen alle Threads betrifft. Wird der Dateizeiger mit lseek() verändert, ist er für alle Threads verändert. Schließt ein Thread eine Datei, erhält ein paralleler Thread einen Fehler, wenn er auf diese Datei zugreift. Sie müssen auch sicherstellen, dass die Bibliotheken, die Sie benutzen, nicht Probleme mit paralleler Abarbeitung haben. Wer mit Threads arbeiten will, muss also nicht nur die Aufrufe kennen, sondern auch sehr sorgfältig arbeiten.

Der Prozess selbst ist immer auch ein Thread. Der Start eines Threads erzeugt also bereits einen zweiten Thread und damit Parallelität. Der Code für diesen zweiten Thread wird als gewöhnliche Funktion geschrieben. Die Funktion wird als Thread gestartet und sobald sie endet, endet auch der Thread. Die Funktion hat einen Zeiger als Parameter. Mit diesem Zeiger können Sie dem Thread beim Start Daten übergeben. Die Thread-Funktion liefert einen Zeiger zurück, mit dem er Daten wieder zurückgeben kann.

Der Aufruf zum Erzeugen eines Threads heißt pthread_create(). Dabei wird ein neuer Thread erzeugt und sofort gestartet. Da der Prozess selbst auch einen Thread darstellt, laufen dann also zwei Threads. Der wichtigste Parameter ist der Name der Funktion, die als Thread laufen soll.

#include <pthread.h>
int  pthread_create(pthread_t *TID, pthread_attr_t *Attribut,
                    void * (*Funktion)(void *), void *Argument);

Der Parameter TID kann man als Thread-ID bezeichnen. Dazu legen Sie eine Variable vom Typ pthread_create an und übergeben deren Adresse an die Funktion. Der zweite Parameter Attritbut kann verwendet werden, um den Wechsel zwischen den Threads zu verändern. Das ist beispielsweise wichtig, wenn Sie den Wechsel in Echtzeit brauchen. Im Normalfall wird hier NULL übergeben. Für nähere Informationen zu den Attributen finden Sie Informationen in der Manpage von pthread_attr_init. Der dritte Parameter ist die Funktion, die den Code für den Thread enthält. Der letzte Parameter zeigt auf die Daten, die dem Thread als Parameter übergeben werden sollen. Werden keine Übergaben gebraucht, kann der Parameter mit NULL besetzt werden.

Der Rückgabewert ist 0, wenn alles in Ordnung ist.

Ein Thread endet, wenn seine Funktion endet. Damit verhält er sich ähnlich wie die Funktion main bei einem Prozess. Der Thread kann sich auch selbst beenden. Dazu gibt es analog zur Funktion exit() die Funktion pthread_exit().

#include <pthread.h>
void pthread_exit(void *Rueckgabe);

Im Gegensatz zu exit(), der nur eine Fehlernummer zurückgibt, kann pthread_exit() einen Zeiger auf Daten zurückgeben. Dies entspricht dem Rückgabewert der Funktion, wenn sie regulär endet.

Diesen Zeiger bekommt derjenige Thread, der mit Hilfe der Funktion pthread_join() den erzeugten Thread wieder einsammelt.

#include <pthread.h>
int pthread_join(pthread_t TID, void **Rueckgabe);

Die Funktion pthread_join() hat zwei Aufgaben. Solange der Thread TID noch läuft wird der aufrufende Thread blockiert. Läuft der Thread nicht mehr, wird der Thread TID mit dem aktuellen Thread wieder zusammengeführt. Die letzten Reste des Threads verschwinden also. Durch den Parameter Rueckgabe ist auch ein Zugriff auf den Rückgabewert des Threads möglich. Dazu muss eine Zeigervariable definiert werden und deren Adresse als zweiter Parameter übergeben werden. Nach Ende des Threads kann über diese Zeigervariable auf die Rückgabedaten referenziert werden. Interessieren Sie sich nicht für den Rückgabewert, setzen Sie den zweiten Parameter auf NULL.

Das folgende Beispiel startet drei Threads. Die Funktion threadPlus enthält den Code für jeden Thread. Der Thread bekommt beim Aufruf einen Namen als Parameter. Dann läuft er in eine Schleife, die endet, wenn die Variable stopp auf einen Wert ungleich 0 gesetzt wird. Damit wird die Variable zur Endebedingung für alle Threads.

Innerhalb des Threads wird die globale Variable Wert erhöht wenn der Name des Threads mit dem Buchstaben A beginnt, ansonsten wird der Inhalt der Variablen erniedrigt.

[Multithreading]
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

int stopp = 0; /* Synchronisation  */
int Wert  = 0; /* Globale Variable */

void *threadPlus(void *Arg)
{
/* Uebergeben wird der Name */
char *Name = (char *)Arg;
int diff; /* Differenz fuer Wertberechnung */

if (Name[0]=='A') {
    diff=1;
  } else {
    diff=-1;
  }

for (;;) {
    if (stopp) break; /* Thread endet durch Setzen von stopp */
    printf("%s: %dn", Name, Wert);
    /* Namen bestimmt, was der Thread tut */
    Wert += diff;
    sleep(1);
  }
  return NULL;
}

int main(void)
{

pthread_t Add1, Add2, Minus1;

/* Erzeuge und starte drei Threads */
  pthread_create(&Add1, NULL, threadPlus, "Add1");
  pthread_create(&Add2, NULL, threadPlus, "Add2");
  pthread_create(&Minus1, NULL, threadPlus, "Minus1");
  sleep(20);  /* lass die Threads arbeiten */
  stopp = 1;  /* beende das Treiben */
  /* Warte auf das Ende der Threads und loese sie auf */
  pthread_join(Add1, NULL );
  pthread_join(Add2, NULL );
  pthread_join(Minus1, NULL );
  return 0;
}

Die drei Threads bekommen jede ihre eigene Thread-ID. Sie werden im Hauptprogramm gestartet. Der Hauptthread legt sich dann für 20 Sekunden schlafen und lässt die Threads addieren und subtrahieren. Durch das Setzen der Variablen stopp löst er das Ende der Threads aus. Danach führt er alle drei Threads wieder zusammen, bzw. wartet deren Ende ab.

Beim Ablauf des Programms erscheint meist Add1, dann Add2 und Minus1 in der normalen Reihenfolge. Man kann aber auch sehen, dass die Reihenfolge wechselt. An der Variablen Wert können Sie erkennen, dass die Veränderung des Wertes davon abhängt, wer gerade an die Reihe kommt.

Zum Linken des Programmes muss die Option -lpthread an den Compileraufruf angehängt werden.



« Message Queues | Prozesse | Signale »
 
 Zum Katalog
Zum Katalog
Wie werde ich UNIX-Guru?
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 UNIX/Linux

PHP 4-Workshop

Einstieg in Python

Perl fürs Web

MySQL 4

GNOME 2.0
 Empfehlung

Einstieg in XML
 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
Info

 MyGalileo
Der Service für registrierte Leser:
Zu MyGalileo
Info



Copyright © Galileo Press GmbH 2003
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.
[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de