Adloun

informatique (MP2I/MPI) · Chapitre 22 · Application directe du cours

volatile n'est pas une synchronisation

Concurrence et synchronisation · niveau 2

É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éclarationessai 1essai 2essai 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.

AttentionEt le type atomique de C11 ne suffit pas non plus, si on l'écrit mal

On pourrait croire que _Atomic int règle tout. Mesuré :

sur un _Atomic int aobtenu
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.

Exercices semblables

Le cours du chapitre · Tous les exercices du chapitre