Synchronisation des Threads

Développement système natif en c/c++ avec win32 ...

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 19
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Synchronisation des Threads

Post by Hydraxx »

## Synchronisation des threads en Win32

Maintenant que tu sais créer des threads, on arrive au **vrai sujet sérieux** :
la **synchronisation**.

Créer des threads, c’est facile.
Les synchroniser correctement, c’est là que commencent :

* les bugs bizarres
* les crashs aléatoires
* les comportements impossibles à reproduire

Donc ce cours est **très important**.

---

### Pourquoi la synchronisation est nécessaire

Tous les threads d’un même processus partagent :

* la mémoire
* les variables globales
* le heap
* les ressources

Si deux threads accèdent **en même temps** à la même ressource :

* le résultat devient imprévisible
* le programme peut planter
* ou pire : fonctionner “presque” correctement

C’est ce qu’on appelle une **race condition**.

La synchronisation ne sert pas à aller plus vite.
Elle sert à **éviter que tout parte en vrille**.

---

## Les objets de synchronisation Win32

Windows fournit plusieurs mécanismes.
Ils n’ont **pas tous le même rôle**.

---

### Critical Section

La **Critical Section** est l’outil le plus utilisé **dans un seul processus**.

Caractéristiques :

* très rapide
* fonctionne uniquement intra-process
* pas visible entre processus

Principe :

* un seul thread peut entrer dans la section protégée
* les autres attendent

Exemple :

Code: Select all

CRITICAL_SECTION cs;

InitializeCriticalSection(&cs);

EnterCriticalSection(&cs);
// section critique
sharedValue++;
LeaveCriticalSection(&cs);

DeleteCriticalSection(&cs);
À utiliser quand :

* tu travailles dans un seul processus
* tu veux de la performance
* tu protèges des variables partagées

---

### Mutex

Le **Mutex** ressemble à une Critical Section, mais en plus lourd.

Différences :

* peut être partagé entre processus
* plus lent
* basé sur un objet noyau

Exemple :

Code: Select all

HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

WaitForSingleObject(hMutex, INFINITE);
// section protégée
ReleaseMutex(hMutex);

CloseHandle(hMutex);
À utiliser quand :

* plusieurs processus doivent accéder à une ressource
* la performance n’est pas critique

---

### SRW Lock (Slim Reader/Writer Lock)

Le **SRW Lock** est plus moderne.

Il permet :

* plusieurs lecteurs en même temps
* un seul écrivain

Très utile quand :

* les lectures sont fréquentes
* les écritures rares

Exemple :

Code: Select all

SRWLOCK lock = SRWLOCK_INIT;

// lecture
AcquireSRWLockShared(&lock);
// lire les données
ReleaseSRWLockShared(&lock);

// écriture
AcquireSRWLockExclusive(&lock);
// modifier les données
ReleaseSRWLockExclusive(&lock);
À utiliser quand :

* tu as beaucoup de lectures
* tu veux de bonnes performances

---

### Semaphore

Le **Semaphore** permet de limiter l’accès à une ressource à **N threads**.

Exemple :

* pool de connexions
* nombre limité de ressources

Exemple :

Code: Select all

HANDLE hSem = CreateSemaphore(NULL, 2, 2, NULL);

WaitForSingleObject(hSem, INFINITE);
// accès à la ressource
ReleaseSemaphore(hSem, 1, NULL);

CloseHandle(hSem);
Ici :

* seulement 2 threads peuvent entrer en même temps

---

### Event

Un **Event** sert à **signaler quelque chose** entre threads.

Il ne protège pas une ressource.
Il sert à dire :
“Tu peux continuer”.

Deux types :

* auto-reset
* manual-reset

Exemple :

Code: Select all

HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

// thread A
SetEvent(hEvent);

// thread B
WaitForSingleObject(hEvent, INFINITE);

CloseHandle(hEvent);
À utiliser pour :

* synchroniser des étapes
* attendre un signal
* coordination entre threads

---

## Attendre : WaitForSingleObject

La fonction **WaitForSingleObject** permet d’attendre :

* un mutex
* un event
* un thread
* un semaphore

Exemple :

Code: Select all

WaitForSingleObject(hThread, INFINITE);
Ici :

* le thread appelant est bloqué
* jusqu’à la fin du thread attendu

---

## Attendre plusieurs objets : WaitForMultipleObjects

Quand tu dois attendre **plusieurs événements**, tu utilises :

Code: Select all

WaitForMultipleObjects(
count,
handles,
FALSE,
INFINITE
);
Exemple :

Code: Select all

HANDLE handles[2] = { hEvent1, hEvent2 };

WaitForMultipleObjects(2, handles, FALSE, INFINITE);
Ici :

* le thread se débloque quand **un des deux** events est signalé

Très utile pour :

* gérer plusieurs threads
* arrêter proprement une boucle
* attendre plusieurs signaux

---

## Erreurs classiques

Créer trop de threads
Utiliser des mutex partout
Oublier de libérer un verrou
Faire des sections critiques trop longues
Mélanger les mécanismes sans comprendre

Résultat :

* deadlocks
* performances catastrophiques
* bugs aléatoires

---

## Deadlock (à comprendre absolument)

Un deadlock arrive quand :

* thread A attend une ressource détenue par B
* thread B attend une ressource détenue par A

Plus rien n’avance.

Bonne pratique :

* toujours prendre les verrous dans le même ordre
* garder les sections critiques courtes
* réfléchir avant de verrouiller

---

## À retenir

La synchronisation est obligatoire dès que plusieurs threads partagent des données.

Critical Section :

* rapide
* intra-process

Mutex :

* inter-process
* plus lent

SRW Lock :

* lecteur / écrivain
* performant

Semaphore :

* limite le nombre d’accès

Event :

* signalisation

WaitForSingleObject :

* attendre un objet

WaitForMultipleObjects :

* attendre plusieurs objets

---

La synchronisation est souvent la partie la plus difficile du multithreading.
Mais quand tu la maîtrises, ton code devient :

* stable
* prévisible
* robuste

Dans les prochains cours, on verra :

* des exemples de race condition réelles
* comment provoquer un deadlock
* comment les détecter
* et comment écrire du code multi-thread propre.

Who is online

Users browsing this forum: No registered users and 1 guest