Pire (ne fonctionnera pas réellement)
Remplacez le modificateur d'accès par counter
parpublic volatile
Comme d'autres l'ont mentionné, cela n'est pas du tout sûr en soi. Le fait volatile
est que plusieurs threads s'exécutant sur plusieurs processeurs peuvent et vont mettre en cache les données et réorganiser les instructions.
Si ce n'est pas le cas volatile
et que le CPU A incrémente une valeur, le CPU B peut ne pas réellement voir cette valeur incrémentée jusqu'à un certain temps plus tard, ce qui peut provoquer des problèmes.
Si c'est le cas volatile
, cela garantit simplement que les deux CPU voient les mêmes données en même temps. Cela ne les empêche pas du tout d'entrelacer leurs opérations de lecture et d'écriture, ce que vous essayez d'éviter.
Deuxième meilleur:
lock(this.locker) this.counter++
;
Ceci est sûr à faire (à condition de vous souvenir de lock
partout où vous accédez this.counter
). Il empêche tout autre thread d'exécuter tout autre code protégé par locker
. L'utilisation de verrous empêche également les problèmes de réorganisation multi-CPU comme ci-dessus, ce qui est génial.
Le problème est que le verrouillage est lent, et si vous réutilisez le locker
dans un autre endroit qui n'est pas vraiment lié, vous pouvez finir par bloquer vos autres threads sans raison.
Meilleur
Interlocked.Increment(ref this.counter);
Ceci est sûr, car il effectue efficacement la lecture, l'incrémentation et l'écriture en un seul coup qui ne peut pas être interrompu. Pour cette raison, cela n'affectera aucun autre code, et vous n'avez pas besoin de vous rappeler de verrouiller ailleurs non plus. C'est aussi très rapide (comme le dit MSDN, sur les processeurs modernes, il s'agit souvent littéralement d'une seule instruction de processeur).
Je ne suis pas tout à fait sûr cependant si cela contourne les autres processeurs réorganisant les choses, ou si vous devez également combiner volatile avec l'incrément.
InterlockedNotes:
- LES MÉTHODES VERROUILLÉES SONT CONCURRENTEMENT SÉCURITAIRES SUR TOUT NOMBRE DE CŒURS OU DE CPU.
- Les méthodes imbriquées appliquent une clôture complète autour des instructions qu'elles exécutent, donc la réorganisation ne se produit pas.
- Les méthodes verrouillées n'ont pas besoin ou même ne prennent pas en charge l'accès à un champ volatil , car volatile est placé une demi-clôture autour des opérations sur un champ donné et verrouillé utilise la clôture complète.
Note de bas de page: Ce qui est réellement bon volatile.
Comme cela volatile
n'empêche pas ce genre de problèmes de multithreading, à quoi ça sert? Un bon exemple est de dire que vous avez deux threads, un qui écrit toujours dans une variable (disons queueLength
) et un qui lit toujours à partir de cette même variable.
S'il queueLength
n'est pas volatile, le thread A peut écrire cinq fois, mais le thread B peut voir ces écritures comme étant retardées (ou même potentiellement dans le mauvais ordre).
Une solution serait de verrouiller, mais vous pouvez également utiliser volatile dans cette situation. Cela garantirait que le thread B verra toujours la chose la plus à jour écrite par le thread A. Notez cependant que cette logique ne fonctionne que si vous avez des écrivains qui ne lisent jamais et des lecteurs qui n'écrivent jamais, et si la chose que vous écrivez est une valeur atomique. Dès que vous effectuez une seule lecture-modification-écriture, vous devez accéder aux opérations verrouillées ou utiliser un verrou.