Usar bloqueo mutex en C

Jinku Hu 12 octubre 2023
Usar bloqueo mutex en C

Este artículo explicará varios métodos de cómo usar el bloqueo mutex en C.

Utilice el tipo pthread_mutex_t y la función pthread_mutex_lock para proteger la sección crítica del código

Los subprocesos comparten espacios de direcciones, lo que implica que las modificaciones a los datos compartidos, como las variables globales, deben sincronizarse; de lo contrario, habrá un comportamiento incorrecto del programa. Tenga en cuenta que el siguiente código crea 4 hilos adicionales con la llamada pthread_create y pasa func3 como punto de partida de su ejecución. func3 modifica la variable global shared con una por una en un bucle for de 10000 iteraciones. Por lo tanto, si los cuatro subprocesos incrementan el valor de shared en 10000 cada uno, el programa debería generar 40000.

Si ejecuta el siguiente código, el resultado será un entero aleatorio, pero no 40000. Este comportamiento se clasifica generalmente como una condición de carrera, lo que implica que determinados subprocesos acceden a la variable compartida sin consultarse entre sí, es decir, sincronización. Es decir, a menudo cuando su ejecución del bucle se entrelaza, se alcanza la inconsistencia en los accesos y almacenes de la variable compartida y, finalmente, se produce una suma incorrecta.

La sección de código donde varios subprocesos modifican el mismo objeto en la memoria se denomina sección crítica. En general, la sección crítica debe protegerse con algún tipo de bloqueo que obligaría a otros subprocesos a esperar hasta que el subproceso actual finalice la ejecución y garantice que todos obtengan el valor incrementado correcto. Mutex es uno de los tipos de bloqueo que se puede utilizar para proteger la sección crítica como este bucle for en 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);
}

Producción :

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

En este caso, utilizaremos la biblioteca de subprocesos POSIX y su tipo integrado pthread_mutex_t. La variable de tipo pthread_mutex_t se suele declarar como duración de almacenamiento static. Mutex debe inicializarse solo una vez antes de usarse. Cuando el mutex se declara como static, se debe usar la macro PTHREAD_MUTEX_INITIALIZER para inicializarlo. Una vez que se inicializa el mutex, los subprocesos pueden utilizar las funciones pthread_mutex_lock y pthread_mutex_unlock correspondientemente. pthread_mutex_lock bloquea el objeto mutex pasado como único argumento. Si el mutex ya estaba bloqueado, el subproceso que realiza la llamada se bloquea hasta que el mutex esté disponible. pthread_mutex_unlock debe llamarse para desbloquear el mutex. Si hay subprocesos esperando en el mismo mutex, la política de programación determina cuál obtiene el bloqueo de objeto. Finalmente, llamamos pthread_join en cada uno de los cuatro hilos e imprimimos el entero - shared, que en este caso debería tener el valor correcto almacenado.

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

Producción :

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