Verwendung von ein Semaphor in C

Jinku Hu 12 Oktober 2023
  1. Verwenden Sie POSIX-Semaphoren, um den Zugriff auf freigegebene Variablen in C zu synchronisieren
  2. Verwenden Sie die Funktion sem_destroy, um unbenanntes Semaphor zu zerstören
Verwendung von ein Semaphor in C

Dieser Artikel zeigt verschiedene Methoden zur Verwendung eines Semaphors in C.

Verwenden Sie POSIX-Semaphoren, um den Zugriff auf freigegebene Variablen in C zu synchronisieren

Auf UNIX-basierten Systemen gibt es zwei gängige Semaphor-APIs - POSIX-Semaphoren und System V-Semaphoren. Letzteres verfügt über eine weniger einfache Benutzeroberfläche und bietet dieselben Funktionen wie die POSIX-API. Beachten Sie, dass Semaphore ein weiterer Synchronisationsmechanismus wie Mutexe sind und in meist ähnlichen Szenarien verwendet werden können. Ein Semaphor ist eine vom Kernel verwaltete Ganzzahl, die normalerweise auf den Anfangswert größer oder gleich 0 gesetzt wird.

An einem Semaphorobjekt können zwei Operationen ausgeführt werden - Inkrementieren oder Dekrementieren um eins, was dem Erfassen und Freigeben der gemeinsam genutzten Ressource entspricht. POSIX bietet einen speziellen Typ sem_t für ein unbenanntes Semaphor, ein häufigeres Werkzeug in Multithread-Workflows. Die Variable sem_t muss mit der Funktion sem_init initialisiert werden, die auch angibt, ob das angegebene Semaphor von Prozessen oder Threads eines Prozesses gemeinsam genutzt werden soll. Sobald die Variable initialisiert ist, können wir die Synchronisation mit den Funktionen sem_post und sem_wait implementieren. sem_post erhöht das Semaphor, was normalerweise dem Entsperren der gemeinsam genutzten Ressource entspricht. Im Gegensatz dazu dekrementiert sem_wait das Semaphor und bezeichnet das Sperren der Ressource. Daher müsste der kritische Abschnitt mit sem_wait beginnen und mit dem Aufruf sem_post enden. Beachten Sie jedoch, dass die Überprüfung des Erfolgsstatuscodes für das Debuggen des Codes von entscheidender Bedeutung sein kann.

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

static long shared = 0;
static sem_t sem;

enum { THREADS = 4 };

#define errExit(msg)    \
  do {                  \
    perror(msg);        \
    exit(EXIT_FAILURE); \
  } while (0)

static void *threadFunc(void *arg) {
  long loops = *((long *)arg);

  for (long j = 0; j < loops; j++) {
    if (sem_wait(&sem) == -1) errExit("sem_wait");

    shared++;

    if (sem_post(&sem) == -1) errExit("sem_post");
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  pthread_t t[THREADS];
  int s;
  long nloops;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s num_loops\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  nloops = strtol(argv[1], NULL, 0);

  if (sem_init(&sem, 0, 1) == -1) errExit("sem_init");

  for (int i = 0; i < THREADS; ++i) {
    s = pthread_create(&t[i], NULL, threadFunc, &nloops);
    if (s != 0) errExit("pthread_create");
  }

  for (int i = 0; i < THREADS; ++i) {
    s = pthread_join(t[i], NULL);
    if (s != 0) errExit("pthread_join");
  }

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

Beispielbefehl:

./program_name 1000

Ausgabe:

shared = 4000

Verwenden Sie die Funktion sem_destroy, um unbenanntes Semaphor zu zerstören

Ein mit einem Aufruf sem_init initialisiertes Semaphor muss mit der Funktion sem_destroy zerstört werden. Beachten Sie jedoch, dass sem_destroy aufgerufen werden sollte, wenn keiner der Prozesse / Threads darauf wartet. Das Weglassen des Aufrufs sem_destroy kann auf einigen Systemen zu einem Speicherverlust führen.

Im Allgemeinen weisen die Semaphore eine ähnliche Leistung im Vergleich zu den Pthread-Mutexen auf, wobei letztere normalerweise für eine bessere Codestruktur bevorzugt werden. Es gibt jedoch einige Szenarien, in denen die Sperre vom Signalhandler geändert werden sollte, was erfordert, dass die Funktion asynchron sicher ist, und nur sem_post als solche implementiert ist. Es gibt auch ein benanntes Semaphor in der POSIX-API, das möglicherweise auch nach dem Beenden eines Threads, der es erstellt und verwendet hat, bestehen bleibt.

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

static long shared = 0;
static sem_t sem;

enum { THREADS = 4 };

#define errExit(msg)    \
  do {                  \
    perror(msg);        \
    exit(EXIT_FAILURE); \
  } while (0)

static void *threadFunc(void *arg) {
  long loops = *((long *)arg);

  for (long j = 0; j < loops; j++) {
    if (sem_wait(&sem) == -1) errExit("sem_wait");

    shared++;

    if (sem_post(&sem) == -1) errExit("sem_post");
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  pthread_t t[THREADS];
  int s;
  long nloops;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s num_loops\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  nloops = strtol(argv[1], NULL, 0);

  if (sem_init(&sem, 0, 1) == -1) errExit("sem_init");

  for (int i = 0; i < THREADS; ++i) {
    s = pthread_create(&t[i], NULL, threadFunc, &nloops);
    if (s != 0) errExit("pthread_create");
  }

  for (int i = 0; i < THREADS; ++i) {
    s = pthread_join(t[i], NULL);
    if (s != 0) errExit("pthread_join");
  }

  printf("shared = %ld\n", shared);

  sem_destroy(&sem);
  exit(EXIT_SUCCESS);
}
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