POSIX Threads (pthreads)

Aus dev.kaibel.net
Version vom 21. Oktober 2025, 15:02 Uhr von PhilKa (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

POSIX Threads (pthreads)

POSIX Threads (kurz: pthreads) sind eine standardisierte Programmierschnittstelle zur Verwendung von Threads in Unix-ähnlichen Betriebssystemen (Linux, macOS, BSD, etc.). Sie sind in der POSIX-Norm (IEEE 1003.1c) definiert und bilden die Grundlage für Multithreading in der Programmiersprache C auf diesen Systemen.

---

Grundprinzip

Ein Thread ist ein leichtgewichtiger Ausführungspfad innerhalb eines Prozesses. Alle Threads eines Prozesses:

  • teilen sich den Adressraum (Variablen, Speicher, offene Dateien),
  • besitzen aber jeweils einen eigenen Stack und Befehlszähler.
+-----------------------------+
|        Prozess              |
|-----------------------------|
|  Code, Daten, Heap, Files   |
|-----------------------------|
|  Thread 1 → Stack1          |
|  Thread 2 → Stack2          |
|  Thread 3 → Stack3          |
+-----------------------------+

→ Dadurch sind Threads effizienter als separate Prozesse, aber erfordern sorgfältige Synchronisation.

---

Header-Datei

Alle pthread-Funktionen sind in der Header-Datei enthalten:

#include <pthread.h>

Beim Kompilieren muss oft die pthread-Bibliothek explizit eingebunden werden:

gcc main.c -o main -pthread

---

Wichtige pthread-Funktionen

Funktion Beschreibung
pthread_create() Erzeugt einen neuen Thread
pthread_exit() Beendet den aktuellen Thread
pthread_join() Wartet auf die Beendigung eines anderen Threads
pthread_detach() Trennt Thread (keine join-Wartung mehr möglich)
pthread_self() Liefert ID des aktuellen Threads
pthread_mutex_init() Initialisiert einen Mutex (Sperre)
pthread_mutex_lock() Sperrt kritischen Bereich
pthread_mutex_unlock() Gibt Sperre frei
pthread_cond_wait() / pthread_cond_signal() Bedingungsvariablen zur Synchronisation

---

Beispiel: Einfacher Thread

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

void* thread_func(void* arg) {
    int id = *(int*)arg;
    printf("Thread %d läuft!\n", id);
    pthread_exit(NULL);
}

int main() {
    pthread_t thread;
    int id = 1;

    // Thread erzeugen
    if (pthread_create(&thread, NULL, thread_func, &id) != 0) {
        perror("pthread_create");
        return EXIT_FAILURE;
    }

    // Warten, bis Thread fertig ist
    pthread_join(thread, NULL);

    printf("Thread beendet.\n");
    return 0;
}

Ergebnis:

Thread 1 läuft!
Thread beendet.

---

Beispiel: Mehrere Threads

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

#define N 5

void* worker(void* arg) {
    int i = *(int*)arg;
    printf("Thread %d: Hallo!\n", i);
    return NULL;
}

int main() {
    pthread_t threads[N];
    int ids[N];

    for (int i = 0; i < N; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], NULL, worker, &ids[i]);
    }

    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], NULL);
    }

    printf("Alle Threads beendet.\n");
    return 0;
}

---

Synchronisation mit Mutex

Da Threads denselben Speicher teilen, können sie gleichzeitig auf dieselben Variablen zugreifen → Race Conditions. Ein Mutex (Mutual Exclusion Lock) schützt kritische Abschnitte.

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

int counter = 0;
pthread_mutex_t lock;

void* increment(void* arg) {
    for (int i = 0; i < 100000; i++) {
        pthread_mutex_lock(&lock);
        counter++;
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_mutex_init(&lock, NULL);

    pthread_create(&t1, NULL, increment, NULL);
    pthread_create(&t2, NULL, increment, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    pthread_mutex_destroy(&lock);

    printf("Endergebnis: %d\n", counter);
    return 0;
}

Ohne Mutex wäre das Ergebnis unbestimmt, weil beide Threads gleichzeitig auf `counter` zugreifen würden.

---

Bedingungsvariablen

Mit pthread_cond_t lassen sich Threads blockieren, bis ein bestimmtes Ereignis eintritt. Das funktioniert immer in Kombination mit einem Mutex.

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

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0;

void* worker(void* arg) {
    pthread_mutex_lock(&lock);
    while (!ready) {
        pthread_cond_wait(&cond, &lock);  // wartet auf Signal
    }
    printf("Thread wurde aktiviert!\n");
    pthread_mutex_unlock(&lock);
    return NULL;
}

int main() {
    pthread_t t;
    pthread_create(&t, NULL, worker, NULL);

    sleep(2);
    pthread_mutex_lock(&lock);
    ready = 1;
    pthread_cond_signal(&cond); // weckt Thread
    pthread_mutex_unlock(&lock);

    pthread_join(t, NULL);
    return 0;
}

---

Thread-Attribute

Mit pthread_attr_t lassen sich Threads konfigurieren:

  • Detach-State (joinable/detached)
  • Stackgröße
  • Scheduling-Parameter

Beispiel:

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &attr, func, NULL);
pthread_attr_destroy(&attr);

---

Detached Threads

Ein „detached“ Thread kann nicht mehr per pthread_join() abgefragt werden – er gibt seine Ressourcen beim Ende automatisch frei. Das ist nützlich für kurzlebige Hintergrundaufgaben.

---

Thread-Sicherheit und Reentranz

  • Thread-safe Funktionen dürfen von mehreren Threads gleichzeitig genutzt werden (z. B. `printf()` meist sicher).
  • Reentrant Funktionen verändern keine globalen Zustände und können unterbrochen werden.

Bei Bedarf stehen sichere Varianten zur Verfügung, z. B.: strtok_r(), asctime_r() usw.

---

Vor- und Nachteile von POSIX Threads

Vorteile Nachteile
Hohe Parallelität auf Mehrkernsystemen Aufwendige Synchronisation
Geringer Overhead gegenüber Prozessen Potenzielle Race Conditions
Gemeinsamer Speicher erleichtert Datenaustausch Fehler schwer zu debuggen
Standardisiert (POSIX-konform) Nicht auf allen Plattformen verfügbar (z. B. Windows ≠ pthreads)

---

Vergleich zu Prozessen

Merkmal Thread Prozess
Speicherraum geteilt getrennt
Startkosten gering hoch
Kommunikation direkt über Speicher über Pipes/Sockets
Stabilität kann andere Threads abstürzen lassen isoliert

---

Typische Fehler

  • Vergessen, `pthread_join()` aufzurufen (führt zu Zombie-Threads)
  • Zugriff auf gemeinsame Daten ohne Mutex
  • Nicht initialisierte Synchronisationsobjekte
  • Rückgabewerte der pthread-Funktionen ignorieren

---

Siehe auch

---

Quellen

  • IEEE Std 1003.1c-1995 – POSIX Threads (pthreads)
  • David R. Butenhof: Programming with POSIX Threads, Addison-Wesley 1997
  • Stevens, W. Richard: Advanced Programming in the UNIX Environment, Prentice Hall
  • Linux man pages: man pthread_create, man pthread_mutex_lock
  • GNU C Library Documentation