Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Les dangers de l’incrémentation / décrémentation en multithreading

[new:30/07/2014]Parfois il est bon de parler de petites choses dans un billet court.. C’est le cas aujourd’hui pour les opérateurs d’incrémentation et de décrémentation qui posent de sérieux problèmes en mode multitâche…

Parallélisme, multithreading, asynchronisme…

Toutes ces technologies ou techniques ont pour finalité de faire tourner plusieurs bouts de code en même temps. Les machines modernes, même mobiles, ayant plusieurs cœurs il s’agit vraiment de faire exécuter du code par plusieurs “ordinateurs” physiquement différents à la fois, même si tout cela est rangé sagement dans un même boitier, dans une même puce.

Dans mon dernier billet je mettais l’accent sur l’utilisation correcte de async/await et la façon d’optimiser le code dans un contexte asynchrone.

Aujourd’hui je voudrais vous alerter du danger de l’utilisation de “++” et “--” dans de tels contextes multitâches.

++ et -- sont dans un bateau multitâche…

Non, la suite n’est pas de savoir lequel des deux tombe à l’eau et lequel reste à bord. Ils coulent tous les deux, tout simplement !

L’implémentation de ces opérateurs n’est pas “thread safe”, c’est à dire que leur utilisation et surtout leur effet dans un contexte multithreadé n’est pas garanti.

Vous allez vite me croire en quelques lignes de C# sous LinqPad sous la forme d’un GIF animé :

incdecthread

Ce code est vraiment très simple : une méthode Main() en mode console qui utilise deux variables statiques, deux compteurs de type integer, appelés counter1 et counter2 en toute logique.

Pour montrer les danger de l’incrémentation (ce serait la même chose pour la décrémentation) dans un contexte multitâches j’utilise System.Threading.Tasks et Parallel.For qui permet de paralléliser des traitements fournis dans une boucle de type For.

J’ai fixé un maximum de 1000 itérations pour la boucle parallélisée. Comme le contenu de la boucle ne fait pas grand chose j’ai ajouté un délai de 12 millisecondes. Cela ralenti un peu les choses et permet de voir l’effet pervers que je cherche à mettre en évidence.

A chaque passage dans la boucle les deux compteurs sont incrémentés par “1”. Le premier utilise l’opérateur d’incrémentation “++”, l’autre utilise Interlocked.Increment().

En fin de travail l’application récapitule le nombre d’itérations effectuées ainsi que la valeur des compteurs.

Et Ô désolante réalité… Si le compteur 2 affiche avec fierté “1000” qui correspond à nos attentes, le compteur 1 semble avoir connu quelques “trous” et montre une valeur de 898 totalement imprévue…

Conclusion

La conclusion s’impose d’elle-même : les opérateurs d’incrémentation et de décrémentation ne sont pas fiables en mode multitâche. Et comme ce mode devient de plus en plus fréquent en programmation moderne il y a beaucoup à parier que de sacrés bogues bien difficiles à trouver ne manqueront pas de causer bien des tracas à de pauvres développeurs mal informés…

Ce n’est pas le cas des lecteurs de Dot.Blog, bien heureusement !

Alors plus que jamais :

Stay Tuned !

blog comments powered by Disqus