volatile n'est pas une synchronisation
Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 22 — Concurrence et synchronisation
Énoncé
On entend souvent qu'il suffit de déclarer une variable partagée volatile. Le mettre à l'épreuve : quatre fils, incréments chacun.
Corrigé
Mesuré, sur quatre fils et incréments attendus :
| déclaration | essai 1 | essai 2 | essai 3 |
|---|---|---|---|
| `int compteur` | |||
| `volatile int compteur` |
Aucune différence. Le volatile n'a rien corrigé, et les deux colonnes sont indiscernables.
Ce que volatile dit vraiment : « ne garde pas cette variable dans un registre, relis-la en mémoire à chaque accès ». C'est une contrainte sur les optimisations du compilateur, faite pour les registres matériels dont la valeur change toute seule. Elle ne dit rien sur l'atomicité.
Or le défaut n'est pas là. compteur = compteur + 1 reste lire, ajouter, écrire : trois opérations, avec ou sans volatile. Un autre fil peut toujours s'intercaler entre la lecture et l'écriture, et l'incrément se perd exactement comme avant.
On pourrait croire que _Atomic int règle tout. Mesuré :
| sur un `_Atomic int a` | obtenu |
|---|---|
| `a = a + 1;` | |
| `atomic_fetch_add` de |
La première ligne est toujours fausse, et c'est instructif. Le type atomique rend atomique chaque lecture et chaque écriture — mais a = a + 1 en fait deux : une lecture atomique, puis une écriture atomique, avec un intervalle entre les deux. Seule une opération de lecture-modification-écriture indivisible, comme atomic_fetch_add, résout le problème.
La conclusion, et c'est la seule à retenir du chapitre : l'atomicité ne se déclare pas sur une variable, elle s'obtient sur une opération. Les deux moyens au programme sont le mutex et le sémaphore, et ils encadrent un morceau de code — pas une donnée. Tout le reste est une croyance.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.