Usar un semáforo en C
- Utilice semáforos POSIX para sincronizar el acceso a variables compartidas en C
-
Utilice la función
sem_destroy
para destruir el semáforo sin nombre
Este artículo demostrará varios métodos sobre cómo usar un semáforo en C.
Utilice semáforos POSIX para sincronizar el acceso a variables compartidas en C
Hay dos API de semáforos comunes en los sistemas basados en UNIX: semáforos POSIX y semáforos System V. Se considera que este último tiene una interfaz menos simple y ofrece las mismas características que la API POSIX. Tenga en cuenta que los semáforos son otro mecanismo de sincronización como las exclusiones mutuas y se pueden utilizar en escenarios casi similares. Un semáforo es un número entero mantenido por el kernel, generalmente establecido en el valor inicial mayor o igual a 0
.
Se pueden realizar dos operaciones en un objeto semáforo: incrementar o disminuir en uno, lo que corresponde a adquirir y liberar el recurso compartido. POSIX proporciona un tipo especial sem_t
para un semáforo sin nombre, una herramienta más común en flujos de trabajo de subprocesos múltiples. La variable sem_t
debe inicializarse con la función sem_init
que también indica si el semáforo dado debe compartirse entre procesos o subprocesos de un proceso. Una vez inicializada la variable, podemos implementar la sincronización mediante las funciones sem_post
y sem_wait
. sem_post
incrementa el semáforo, que normalmente corresponde al desbloqueo del recurso compartido. Por el contrario, sem_wait
disminuye el semáforo y denota el bloqueo del recurso. Por lo tanto, la sección crítica debería comenzar con sem_wait
y terminar con la llamada sem_post
. Sin embargo, tenga en cuenta que la verificación del código de estado de éxito puede ser esencial para depurar el código.
#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);
}
Comando de muestra:
./program_name 1000
Producción :
shared = 4000
Utilice la función sem_destroy
para destruir el semáforo sin nombre
Un semáforo inicializado con una llamada sem_init
debe destruirse utilizando la función sem_destroy
. Sin embargo, tenga en cuenta que se debe llamar a sem_destroy
cuando ninguno de los procesos / subprocesos lo esté esperando. Omitir la llamada sem_destroy
puede resultar en una pérdida de memoria en algunos sistemas.
Generalmente, los semáforos tienen un rendimiento similar en comparación con los mutex de Pthread, pero este último suele ser el preferido para una mejor estructura de código. Sin embargo, hay algunos escenarios en los que el bloqueo debe modificarse desde el manejador de señales, lo que requiere que la función sea asincrónica segura, y solo se implementa sem_post
como tal. También hay un semáforo con nombre en la API POSIX, que puede persistir incluso después de que finalice un hilo que lo creó y lo usó.
#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