Verwendung von ein Semaphor in C
- Verwenden Sie POSIX-Semaphoren, um den Zugriff auf freigegebene Variablen in C zu synchronisieren
-
Verwenden Sie die Funktion
sem_destroy
, um unbenanntes Semaphor zu zerstören
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);
}
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