2. Communication, partage et conditions de concurrence

L’entrelacement est provoqué : une barrière oblige les deux threads à lire 0 avant leur écriture. Chacun écrit donc 1.
- Deux lectures de 0 conduisent à deux écritures de 1.
- C’est une démonstration Python contrôlée, pas une prédiction du comportement d’un programme C avec une data race.
Lire la capture en texte
2 threads : local = compteur ; barrière ; compteur = local + 1
Valeurs lues par les deux threads : [0, 0]
Deux incréments attendus : 2
Résultat observé : 1
Entrelacement forcé : lecture / rendez-vous / écriture.Démonstration Station Minuit · Ubuntu 24.04 / WSL2 · Python 3 · 15 octobre 2026 (Toronto). Sortie réelle remise en page et capturée; exemple ajouté, distinct du matériel officiel.
2.1 Processus et threads
Deux processus possèdent normalement des espaces d’adressage distincts. Pour communiquer, ils peuvent notamment employer :
- une mémoire partagée, rapide mais exigeant une synchronisation explicite;
- un tube (pipe) créé par un processus parent, canal FIFO généralement unidirectionnel;
- d’autres mécanismes du système, selon le besoin.
Les threads d’un même processus partagent déjà le code, les données et plusieurs ressources. Le problème n’est donc pas de franchir deux espaces d’adressage, mais de contrôler les accès concurrents à cet état commun.
Un canal de communication ne résout pas automatiquement le problème de synchronisation. Transférer une donnée, protéger une structure et ordonner producteur et consommateur sont trois besoins distincts.
2.2 Définition d’une condition de concurrence
Une condition de concurrence (race condition) existe lorsque le résultat dépend de l’ordre précis dans lequel des opérations concurrentes lisent ou modifient un état partagé. Le programme peut sembler correct lors de centaines d’essais, puis échouer sous une charge ou un ordonnancement différent.
Une instruction de haut niveau n’est pas nécessairement atomique. Par exemple :
Code (c) · sélectionner pour copier
count++;
peut devenir conceptuellement :
Code (c) · sélectionner pour copier
tmp = count;
tmp = tmp + 1;
count = tmp;
Deux threads peuvent lire la même ancienne valeur, calculer chacun le même résultat et l’écrire successivement. Un incrément est alors perdu.
Dans l’exemple du cours, chaque thread exécute N_ITER incréments. Sans synchronisation, 2 * N_ITER est la valeur attendue mais non garantie; la borne pédagogique annoncée est N_ITER < count < 2 * N_ITER. Cette borne illustre les pertes possibles, sans transformer un résultat particulier en garantie générale d’ordonnancement.
2.3 Exemple du spouleur d’impression
Deux processus A et B consultent le même indice indiquant la prochaine case libre d’une file d’impression :
- A lit l’indice, puis est interrompu.
- B lit le même indice, inscrit son fichier et avance l’indice.
- A reprend avec sa copie périmée, écrit dans la même case et remplace l’entrée de B.
La faute ne provient pas d’une mauvaise valeur initiale, mais du fait que « lire l’indice, écrire l’entrée, incrémenter » n’est pas une opération atomique. La portion qui doit rester cohérente constitue la section critique.