Use um semáforo em C
- Use semáforos POSIX para sincronizar o acesso a variáveis compartilhadas em C
-
Use a função
sem_destroy
para destruir o semáforo sem nome
Este artigo demonstrará vários métodos sobre como usar um semáforo em C.
Use semáforos POSIX para sincronizar o acesso a variáveis compartilhadas em C
Existem duas APIs de semáforo comuns em sistemas baseados em UNIX - semáforos POSIX e semáforos System V. Este último é considerado como tendo uma interface menos simples, mas oferecendo os mesmos recursos da API POSIX. Observe que os semáforos são outro mecanismo de sincronização, como mutexes, e podem ser utilizados em cenários semelhantes. Um semáforo é um inteiro mantido pelo kernel, geralmente definido com o valor inicial maior ou igual a 0
.
Duas operações podem ser feitas em um objeto semáforo - incremento ou decremento em um, que corresponde à aquisição e liberação do recurso compartilhado. POSIX fornece um tipo especial sem_t
para um semáforo sem nome, uma ferramenta mais comum em fluxos de trabalho multithread. A variável sem_t
deve ser inicializada com a função sem_init
que também indica se o semáforo dado deve ser compartilhado entre processos ou threads de um processo. Uma vez que a variável é inicializada, podemos implementar a sincronização usando as funções sem_post
e sem_wait
. sem_post
incrementa o semáforo, que geralmente corresponde ao desbloqueio do recurso compartilhado. Em contraste, sem_wait
diminui o semáforo e denota o bloqueio do recurso. Assim, a seção crítica precisaria começar com sem_wait
e terminar com chamada sem_post
. Lembre-se, porém, de que a verificação do código de status de sucesso pode ser essencial para depurar o 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 amostra:
./program_name 1000
Resultado:
shared = 4000
Use a função sem_destroy
para destruir o semáforo sem nome
Um semáforo inicializado com uma chamada sem_init
deve ser destruído usando a função sem_destroy
. Observe, entretanto, que sem_destroy
deve ser chamado quando nenhum dos processos / threads estiver esperando por ele. A omissão da chamada sem_destroy
pode resultar em um vazamento de memória em alguns sistemas.
Geralmente, os semáforos têm um desempenho semelhante em comparação com os mutexes Pthread, mas o último geralmente é preferido para uma melhor estrutura de código. No entanto, existem alguns cenários onde o bloqueio deve ser modificado a partir do manipulador de sinal, o que requer que a função seja assíncrona segura, e apenas sem_post
é implementado como tal. Também existe um semáforo nomeado na API POSIX, que pode persistir mesmo após o encerramento de uma thread que o criou e o utilizou.
#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