Différence entre un mutex et un sémaphore comptant

Définition et rôle

Mutex (Mutual Exclusion) :
Un mutex est un mécanisme de synchronisation binaire (deux états : verrouillé ou déverrouillé).
Il garantit que seul un thread à la fois peut accéder à une ressource critique (exclusion mutuelle).
Il est généralement associé à un propriétaire (le thread qui l'a verrouillé) et ne peut être libéré que par ce même thread.
Utilisé pour protéger des sections critiques dans un contexte multithread.

Sémaphore comptant (Counting Semaphore) :
Un sémaphore comptant est un mécanisme de synchronisation qui maintient un compteur (valeur entière ≥ 0).
Il permet à plusieurs threads (jusqu'à la valeur du compteur) d'accéder simultanément à une ressource.
Contrairement au mutex, il n'a pas de notion de propriétaire : n'importe quel thread peut libérer le sémaphore.
Utilisé pour gérer des ressources partagées en quantité limitée (ex. : pool de connexions, accès à un buffer).

Comparaison technique

Critère Mutex Sémaphore comptant

État Binaire (0 ou 1) Entier (≥ 0)
Propriétaire Oui (le thread qui verrouille) Non
Libération Uniquement par le propriétaire Par n'importe quel thread
Utilisation typique Exclusion mutuelle stricte Gestion de ressources multiples
Exemple Protection d'une variable globale Limiter l'accès à un pool de threads

Comportement en cas de contention

Mutex :
Si un thread tente de verrouiller un mutex déjà pris, il est bloqué jusqu'à ce que le mutex soit libéré par son propriétaire.
Risque de deadlock si le propriétaire ne libère pas le mutex (ex. : plantage du thread).

Sémaphore comptant :
Si le compteur est à 0, les threads sont bloqués jusqu'à ce qu'un autre thread incrémente le sémaphore (via sem_post ou équivalent).
Permet une gestion plus flexible des accès concurrents.

Implémentation (exemples en C/POSIX)

Mutex :
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex); // Verrouille
// Section critique
pthread_mutex_unlock(&mutex); // Déverrouille

Sémaphore comptant :
sem_t sem;
sem_init(&sem, 0, 3); // Initialise avec un compteur = 3
sem_wait(&sem); // Décrémente (bloque si compteur = 0)
// Accès à la ressource
sem_post(&sem); // Incrémente

Cas d'usage

Mutex :
Protection d'une variable partagée.
Synchronisation d'un accès exclusif (ex. : écriture dans un fichier).

Sémaphore comptant :
Limiter le nombre de threads dans un pool.
Synchroniser des producteurs/consommateurs (ex. : buffer circulaire).

Pièges et bonnes pratiques

Mutex :
Toujours libérer le mutex (même en cas d'erreur), sinon risque de deadlock.
Éviter les verrouillages imbriqués (sauf avec des mutex récursifs).

Sémaphore comptant :
Ne pas oublier d'incrémenter après usage, sinon les ressources deviennent indisponibles.
Préférer les sémaphores pour des ressources multiples, les mutex pour l'exclusion stricte.

Résumé :
Un mutex est un verrou binaire avec propriétaire, idéal pour l'exclusion mutuelle.
Un sémaphore comptant est un compteur de ressources, utile pour gérer des accès multiples.