10. État partagé et thread pool
Sections
Réglages de lecture
Voix naturelle enregistrée pour ce cours, la même dans tous les navigateurs.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Un bug concurrent ne demande pas un matériel malchanceux. Deux workers peuvent lire la même valeur, calculer le même successeur puis l’écrire. L’exemple exécutable force ce scheduling avec une barrière : deux incréments laissent 1.
Protéger l’invariant, pas la ligne
Section intitulée « Protéger l’invariant, pas la ligne »System.Threading.Lock est la primitive moderne de .NET. On l’utilise quand plusieurs lectures et écritures forment un invariant. Interlocked convient mieux à un compteur atomique ou à une boucle compare-and-swap. Une collection concurrente sécurise sa propre opération, pas une séquence « vérifier, appeler un service, mettre à jour ».
two increments without synchronization: 1Lock count: 10000Interlocked count: 10000AddOrUpdate count: 1000ConcurrentDictionary.AddOrUpdate peut appeler une factory plusieurs fois sous contention. Les factories doivent rester pures ; les effets deviennent une opération séparée et idempotente.
Le parallélisme est un budget
Section intitulée « Le parallélisme est un budget »Parallel.ForEachAsync borne les corps actifs avec MaxDegreeOfParallelism. Le programme observe deux corps pour une limite de deux. C’est un budget local, pas une backpressure end-to-end : le pool de connexions, les sockets et les files du broker ont leurs propres limites.
La starvation apparaît quand des threads bloquent pendant que le travail qui les débloquerait attend dans le thread pool. Préférer les attentes asynchrones pour l’I/O, borner le CPU et mesurer avec dotnet-counters avant d’augmenter les minimums.
Exécuter la preuve
Section intitulée « Exécuter la preuve »dotnet run --project code/csharp-advanced/Advanced -c Release -- l10La sortie portable est dans expected/l10.txt.
Exercices
Section intitulée « Exercices »- Remplacer le compteur par un invariant à deux champs : total et checksum. Montrer pourquoi deux appels
Interlockedne rendent pas la paire atomique. - Mettre un appel HTTP dans
GetOrAdd, compter les appels sous contention, puis supprimer cet effet. - Comparer des degrés 2, 8 et 64 devant une dépendance limitée à quatre appels.
Solutions
- Protéger les deux champs avec un seul
Lock, ou remplacer atomiquement une valeur immuable. Deux écritures atomiques indépendantes exposent un état intermédiaire. - La factory peut s’exécuter plusieurs fois même si une seule valeur gagne. L’effet doit être idempotent et indépendant du primitive de collection.
- Le bon degré vient du débit et de la latence de queue mesurés. Le nombre de CPU ne définit pas la capacité de la dépendance.