Use Mutex Lock em C

Jinku Hu 12 outubro 2023
Use Mutex Lock em C

Este artigo irá explicar vários métodos de como usar o bloqueio mutex em C.

Use o tipo pthread_mutex_t e a função pthread_mutex_lock para proteger a seção crítica do código

Threads compartilham espaços de endereço, o que implica que as modificações nos dados compartilhados, como variáveis ​​globais, devem ser sincronizadas; caso contrário, haverá um comportamento incorreto do programa. Observe que o código a seguir cria 4 threads adicionais com a chamada pthread_create e passa func3 como ponto de partida de sua execução. func3 modifica a variável global compartilhada com uma por uma em um loop for de 10000 iterações. Portanto, se os quatro threads incrementarem o valor de compartilhado em 10000 cada, o programa deve gerar 40000.

Se você executar o código a seguir, o resultado será algum número inteiro aleatório, mas não 40000. Esse comportamento é geralmente classificado como uma condição de corrida, o que implica que determinados threads acessam a variável compartilhada sem consultar uns aos outros, ou seja, sincronização. Ou seja, frequentemente quando sua execução do loop intercala, a inconsistência é alcançada nos acessos e armazenamentos da variável compartilhada e, finalmente, uma soma incorreta é produzida.

A seção de código onde vários threads modificam o mesmo objeto na memória é chamada de seção crítica. Geralmente, a seção crítica deve ser protegida com algum tipo de bloqueio que forçaria outros encadeamentos a esperar até que o encadeamento atual termine a execução e garanta que todos obtenham o valor incrementado correto. Mutex é um dos tipos de bloqueio que podem ser utilizados para proteger a seção crítica como este loop for em func3.

#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);
}

Resultado:

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

Neste caso, utilizaremos a biblioteca de threads POSIX e seu tipo integrado pthread_mutex_t. A variável do tipo pthread_mutex_t é geralmente declarada como duração de armazenamento static. Mutex deve ser inicializado apenas uma vez antes de ser usado. Quando o mutex é declarado como static, deve-se usar a macro PTHREAD_MUTEX_INITIALIZER para inicializá-lo. Uma vez que o mutex é inicializado, os threads podem usar as funções pthread_mutex_lock e pthread_mutex_unlock correspondentemente. pthread_mutex_lock bloqueia o objeto mutex passado como o único argumento. Se o mutex já estiver bloqueado, o thread de chamada será bloqueado até que o mutex se torne disponível. pthread_mutex_unlock deve ser chamado para desbloquear o mutex. Se houver threads esperando no mesmo mutex, a política de agendamento determina qual delas obtém o bloqueio do objeto. Finalmente, chamamos pthread_join em cada uma das quatro threads e imprimimos o inteiro - shared, que neste caso deve ter o valor correto armazenado.

#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);
}

Resultado:

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