/* 05Jun24: Beispiel zur Verwendung von POSIX Threads (Fäden) in C, * anhand von Motiv Matrix-Vektor-Multiplikation. */ #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <pthread.h> #include "sem.h" #define N 100 /* Das ist hier nur ein Makro um leicht ein Array der Größe N * auszugeben, mit dem Namen der Variable. */ #define DUMP(arr) \ do { \ printf("%s:", #arr); \ for (int i = 0; i < N; i++) \ printf(" %g", arr[i]); \ puts(""); \ } while (0) /* a: Die Eingabe Matrix, * b: Der Eingabe Vektor, * c: Der Ausgabe Vektor (a × c) */ static double a[N][N], b[N], c[N]; /* Eine Semaphore, welche wir in diesem Programm dazu nutzen um * gegenseitigen Ausschluss umzusetzen. Diese wird Anfangs auf 1 * initialisiert, und verwaltet damit wie viele Fäden zu einem * Zeitpunkt auf gemeinsamen Speicher arbeiten dürfen. */ static SEM *lock; static void init() { /* Wir initialisieren a auf eine Einheits-Matrix */ for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { a[i][j] = i == j ? 1 : 0; } b[i] = i+1; } /* Und benutzen die Funktion aus der sem.h Schnittstelle, um eine * Semaphore anzulegen. */ lock = semCreate(1); /* TODO: Fehlerbehandlung */ } /* Implementierung der Matrix-Vektor Multiplikation, für eine Reihe * ROW, wo die Summe aus dieser Reihe in der Variable TSUM aufadiert * wird. */ static void mvmult(int row, double *tsum) { double sum = 0; for (int col = 0; col < N; col++) { sum += a[row][col] * b[row]; } c[row] = sum; P(lock); *tsum += sum; V(lock); } /* Wir definieren einen eigenen Datentypen, um dem Übergabeparameter * für einen neuen Thread mit der main-Funktion `multiplier' mehr * Struktur zu geben. */ struct mjob { int row; double *sum; }; /* Und hier ist die Funktion, welche aufgerufen wird von einem neuen * Thread. Es benutzt wiederhin den gleichen Speicher, muss aber den * void*-Zeiger erst uminterpretieren. Die eigentliche Arbeit wird in * diesem Fall, nachdem wir das Argument richtig interpretiert haben * an `mvmult' weitergegeben. */ void *multiplier(void *arg) { struct mjob *mjob = arg; mvmult(mjob->row, mjob->sum); return NULL; } int main() { init(); /* initialise a, b and c */ DUMP(b); /* Der Datentyp `pthread_t' ist vergleichbar zu `pid_t' für * Unix-Prozesse. Wir haben damit eine Weise, wir wir der * Pthread-Bibliothek mitgeben können, um uns bei Operationen auf * spezifische Fäden zu beziehen (in diesem Fall wird es sein, * dass wir darauf warten wollen, bis ein Thread terminiert). */ pthread_t tids[N]; double sum = 0; for (int i = 0; i < N; i++) { /* Argumente für die Threads brauchen alle ihren eigenen * Speicher, weil wir uns nicht darauf verlassen können in * welcher Reihenfolge diese Ausgeführt werden. */ struct mjob *mjob = malloc(sizeof(struct mjob)); if (mjob == NULL) { perror("malloc"); exit(EXIT_FAILURE); } *mjob = (struct mjob) { .row = i, .sum = &sum }; /* Hier werden die Threads erstellt. Wir speichern uns hier * alle TIDs ab, weil wir die später noch benutzen müssen * (ansonsten könnte man die innerhalb von der Schleife * anlegen und verwerfen), und müssen ansonsten nur die * Main-Funktion für den Thread wie auch das Argument * angeben: */ errno = pthread_create(&tids[i], NULL, multiplier, mjob); if (errno != 0) { perror("pthread_create"); exit(EXIT_FAILURE); } } /* Nachdem die Threads erstellt wurde, warten wir in dem * Haupt-Thread nur noch darauf, dass sich alle beenden, damit * der Prozess nicht frühzeitig beendet wird. Das machen * dadurch, dass wir nacheinander darauf warten, bis sich alle * threads beenden. Wenn sich für n > m, Thread-n vor Thread-m * beendet, ist das kein Problem, weil `pthread_join' dann gleich * zurückkehrt. */ for (int i = 0; i < N; i++) { pthread_join(tids[i], NULL); } DUMP(c); /* Das Ergebnis der Summe hier wird nicht stimmen, wenn wir oben * die Semaphore nicht benutzt hätten. Man kann damit Spielen, * wie bei mehr Threads und ohne die Semaphore das Ergebnis * variiert. Hier kann es auch nutzlich sein -fsanitize=thread * beim Übersetzen anzugeben, damit zur Laufzeit * Wettlaufsitiationen erkannt und gemeldet werden. */ printf("total sum: %g\n", sum); semDestroy(lock); return 0; }