Mutex-Sperre in C verwenden

Jinku Hu 12 Oktober 2023
Mutex-Sperre in C verwenden

Dieser Artikel erklärt verschiedene Methoden, wie man Mutex-Lock in C verwendet.

Verwenden Sie den Typ pthread_mutex_t und die Funktion pthread_mutex_lock, um den kritischen Abschnitt des Codes zu schützen

Threads teilen sich Adressräume, was bedeutet, dass Änderungen an den gemeinsam genutzten Daten wie globalen Variablen synchronisiert werden müssen; andernfalls kommt es zu einem fehlerhaften Programmverhalten. Beachten Sie, dass der folgende Code 4 zusätzliche Threads mit dem Aufruf pthread_create erzeugt und func3 als Startpunkt für deren Ausführung übergibt. func3 modifiziert die globale Variable shared nacheinander in einer for-Schleife mit 10000 Iterationen. Wenn also die vier Threads den Wert von shared um jeweils 10000 inkrementieren, sollte das Programm 40000 ausgeben.

Wenn Sie den folgenden Code ausführen, wird das Ergebnis irgendeine zufällige Ganzzahl sein, aber nicht 40000. Dieses Verhalten wird allgemein als Race Condition klassifiziert, was bedeutet, dass bestimmte Threads auf die gemeinsame Variable zugreifen, ohne sich gegenseitig zu konsultieren, d. h. Synchronisation. Wenn sich nämlich ihre Ausführung der Schleife überschneidet, kommt es oft zu Inkonsistenzen bei den Zugriffen auf die gemeinsame Variable und deren Speicherung, und schließlich wird eine falsche Summe ausgegeben.

Der Codeabschnitt, in dem mehrere Threads dasselbe Objekt im Speicher modifizieren, wird als kritischer Abschnitt bezeichnet. Im Allgemeinen sollte der kritische Abschnitt mit einer Art von Sperre geschützt werden, die andere Threads dazu zwingt, zu warten, bis der aktuelle Thread die Ausführung beendet hat, und sicherstellt, dass sie alle den korrekten inkrementierten Wert erhalten. Mutex ist einer der Sperrtypen, der verwendet werden kann, um den kritischen Abschnitt wie diese for-Schleife in func3 zu schützen.

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int shared = 0;

void* func3(void* param) {
  printf("Incrementing the shared variable...\n");
  for (int i = 0; i < 10000; ++i) {
    shared += 1;
  }
  return 0;
}

int main() {
  pthread_t threads[NUM_THREADS];

  for (int i = 0; i < NUM_THREADS; ++i) {
    pthread_create(&threads[i], NULL, func3, NULL);
  }

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

  printf("%d\n", shared);
  exit(EXIT_SUCCESS);
}

Ausgabe:

Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
30384

In diesem Fall verwenden wir die POSIX-Thread-Bibliothek und ihren eingebauten Typ pthread_mutex_t. Die Variable vom Typ pthread_mutex_t wird normalerweise als statische Speicherdauer deklariert. Der Mutex sollte nur einmal initialisiert werden, bevor er verwendet wird. Wenn der Mutex als static deklariert ist, sollte man das Makro PTHREAD_MUTEX_INITIALIZER verwenden, um ihn zu initialisieren. Sobald der Mutex initialisiert ist, können Threads die Funktionen pthread_mutex_lock und pthread_mutex_unlock entsprechend verwenden. pthread_mutex_lock sperrt das als einziges Argument übergebene Mutex-Objekt. Wenn der Mutex bereits gesperrt war, wird der aufrufende Thread blockiert, bis der Mutex wieder verfügbar ist. Zum Entsperren des Mutex sollte pthread_mutex_unlock aufgerufen werden. Wenn es Threads gibt, die auf denselben Mutex warten, bestimmt die Scheduling-Policy, welcher Thread die Objektsperre erhält. Zum Schluss rufen wir pthread_join auf jedem der vier Threads auf und geben die Ganzzahl - shared - aus, die in diesem Fall den richtigen Wert gespeichert haben sollte.

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int shared = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* func3(void* param) {
  pthread_mutex_lock(&mutex);
  printf("Incrementing the shared variable...\n");
  for (int i = 0; i < 10000; ++i) {
    shared += 1;
  }
  pthread_mutex_unlock(&mutex);
  return 0;
}

int main() {
  pthread_t threads[NUM_THREADS];

  for (int i = 0; i < NUM_THREADS; ++i) {
    pthread_create(&threads[i], NULL, func3, NULL);
  }

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

  printf("%d\n", shared);
  exit(EXIT_SUCCESS);
}

Ausgabe:

Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
40000
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook