C++ Multithreading meistern: Effiziente nebenläufige Programmierung mit POSIX Threads
Grundlagen: Multitasking vs. Multithreading
Multithreading ist eine spezialisierte Form des Multitaskings. Während prozessbasiertes Multitasking die gleichzeitige Ausführung eigenständiger Programme verwaltet, ermöglicht threadbasiertes Multitasking die nebenläufige Ausführung verschiedener Teile desselben Programms.
Ein multithreaded Programm besteht aus zwei oder mehr Threads, die parallel laufen können. Jeder Thread definiert einen eigenen Ausführungspfad. C++ (vor C++11) bietet keine eingebaute Thread-Unterstützung, sondern greift vollständig auf das Betriebssystem zurück. Dieses Tutorial nutzt POSIX Threads (pthreads), die auf Linux, macOS, FreeBSD, Solaris und anderen Unix-ähnlichen Systemen verfügbar sind.
Threads erstellen: pthread_create()
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
| Parameter | Beschreibung |
|---|---|
thread |
Zeiger auf pthread_t; erhält die eindeutige Thread-ID. |
attr |
Attribut-Objekt für Thread-Eigenschaften (z. B. Joinable/Detached), oder NULL für Standardwerte. |
start_routine |
Funktionszeiger auf die Thread-Startfunktion (Signatur: void* func(void*)). |
arg |
Einzelnes Argument, das an start_routine übergeben wird (als void*). |
Threads sind Peers – es gibt keine Hierarchie. Jeder Thread kann weitere Threads erzeugen.
Threads beenden: pthread_exit()
void pthread_exit(void *retval);
Beendet den aufrufenden Thread explizit. Wenn main() vor seinen Child-Threads endet und pthread_exit() aufruft, laufen die anderen Threads weiter. Andernfalls werden sie beim Beenden von main() automatisch terminiert.
Beispiel: 5 Threads erzeugen und beenden
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void* PrintHello(void* threadid) {
long tid = (long)threadid;
cout << "Hello World! Thread ID: " << tid << endl;
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
int rc;
for (int i = 0; i < NUM_THREADS; ++i) {
cout << "main(): erzeuge Thread " << i << endl;
rc = pthread_create(&threads[i], NULL, PrintHello, (void*)i);
if (rc) {
cerr << "Fehler: Thread-Erstellung fehlgeschlagen, Code " << rc << endl;
exit(-1);
}
}
pthread_exit(NULL); // Wartet nicht auf Threads, lässt sie aber laufen
return 0;
}
Kompilieren: g++ -o thread_demo thread_demo.cpp -lpthread
Argumente an Threads übergeben
Da start_routine ein void* erwartet, können Sie beliebige Daten über eine Struktur übergeben:
struct ThreadData {
int thread_id;
const char* message;
};
void* PrintHello(void* arg) {
ThreadData* data = static_cast<ThreadData*>(arg);
cout << "Thread ID: " << data->thread_id
<< ", Nachricht: " << data->message << endl;
pthread_exit(NULL);
}
Threads joinen und detach'en
Zwei zentrale Routinen steuern den Lebenszyklus:
pthread_join(thread, &status)– Blockiert den Aufrufer, bis der angegebene Thread endet. Nur joinable Threads können gejoint werden.pthread_detach(thread)– Markiert Thread als detached; Ressourcen werden automatisch bei Beendigung freigegeben. Ein detached Thread kann nicht gejoint werden.
Beispiel: pthread_join() für synchrones Warten
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void* wait(void* t) {
long tid = (long)t;
sleep(1);
cout << "Thread " << tid << " beendet sich." << endl;
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void* status;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_create(&threads[i], &attr, wait, (void*)i);
}
pthread_attr_destroy(&attr);
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_join(threads[i], &status);
cout << "Main: Thread " << i << " beendet mit Status " << status << endl;
}
cout << "Main: Programmende." << endl;
pthread_exit(NULL);
}
Moderne Alternative: C++11 <thread>
Seit C++11 bietet die Standardbibliothek plattformunabhängige Thread-Unterstützung (std::thread, std::mutex, std::condition_variable, …). Für neue Projekte wird die Nutzung der C++-Standardbibliothek empfohlen, da sie typsicherer und portabler ist.
C Sprache
- C++-Operatoren meistern: Ein vollständiger Leitfaden mit praktischen Beispielen
- C++-Kommentare: Best Practices für lesbaren, wartbaren Code
- C++ Klassentemplates meistern: Ein praktischer Leitfaden
- C++ Programmiersprache: Überblick & Kernfeatures
- C++ Konstanten & Literale: Typen, Syntax & Best Practices
- C++ Operatoren: Typen, Nutzung & Best Practices
- C++ Numerische Typen: Praktischer Leitfaden
- C++ Referenzen: Konzepte & Unterschiede zu Pointern
- C++ Templates: Generische Programmierung meistern
- C++ Multithreading meistern: Effiziente nebenläufige Programmierung mit POSIX Threads