66 votes

Comment lire correctement un champ int Interlocked.Increment'ed?

Supposons que j'ai un non-volatile int champ, et un fil qui Interlocked.Increments il. Peut-être une autre thread en toute sécurité lire directement, ou de ne le lire aussi besoin d'être interloqué?

Auparavant, je pensais que je devais utiliser un contrefil lire garantir que je suis de voir la valeur actuelle, puisque, après tout, le champ n'est pas volatile. J'ai été en utilisant Interlocked.CompareExchange(int, 0, 0) pour l'atteindre.

Cependant, je suis tombé sur cette réponse qui suggère que la réalité de la plaine lit toujours voir la version actuelle de l' Interlocked.Incremented valeur, et depuis int lecture est déjà atomique, il n'y a pas besoin de faire quelque chose de spécial. J'ai également constaté une demande dans laquelle Microsoft rejette une demande de Contrefil.Lire(ref int), plus loin en suggérant que c'est complètement superflu.

Donc, puis-je vraiment en toute sécurité lire le plus de valeur actuelle d'un tel int sans Interlocked?

22voto

mgronber Points 2429

Si vous voulez vous assurer que l'autre thread va lire la dernière valeur, vous devez utiliser Thread.VolatileRead(). (*)

L'opération de lecture elle-même est atomique de manière à ne pas causer de problèmes, mais sans volatils lire vous pouvez obtenir de l'ancienne valeur dans le cache ou le compilateur peut optimiser votre code et d'éliminer l'opération de lecture tout à fait. À partir du compilateur, point de vue, c'est assez pour que le code fonctionne en mono-thread de l'environnement. Volatile opérations et les barrières de la mémoire sont utilisés pour limiter la capacité du compilateur d'optimiser et de réorganiser le code.

Il y a plusieurs participants, qui peuvent modifier le code: compilateur JIT-compilateur et de l'UC. Il n'a pas vraiment d'importance dont l'un d'eux montre que votre code est cassé. La seule chose importante est de la .NET modèle de mémoire qu'elle précise les règles qui doivent être respectées par tous les participants.

(*) Thread.VolatileRead() n'a pas vraiment d'obtenir la dernière valeur. Il va lire la valeur et ajouter une barrière de mémoire après la lecture. La première lecture volatile peut obtenir la valeur mise en cache, mais la seconde serait d'obtenir une mise à jour de la valeur parce que la barrière de la mémoire de la première lecture volatile a forcé une mise à jour du cache si c'était nécessaire. Dans la pratique, ce détail a peu d'importance lors de l'écriture du code.

17voto

Markus Points 933

Un peu une question méta, mais un bon aspect à propos de l'aide d' Interlocked.CompareExchange(ref value, 0, 0) (en ignorant l'inconvénient évident que c'est plus difficile à comprendre lorsqu'il est utilisé pour la lecture), c'est qu'il fonctionne indépendamment de l' int ou long. C'est vrai qu' int lectures sont toujours atomique, mais long lectures ne sont pas ou peut être pas, en fonction de l'architecture. Malheureusement, Interlocked.Read(ref value) ne fonctionne que si value est de type long.

Prenons le cas que vous commencez avec un int champ, ce qui rend impossible l'utilisation d' Interlocked.Read(), de sorte que vous pourrez lire directement la valeur au lieu puisque c'est atomique de toute façon. Cependant, plus tard dans le développement, vous ou quelqu'un d'autre décide qu'un long est nécessaire - le compilateur ne pas vous mettre en garde, mais maintenant vous pouvez avoir un bug subtil: L'accès en lecture n'est pas garantie atomique plus. J'ai trouvé à l'aide de Interlocked.CompareExchange() la meilleure alternative ici; Elle peut être inférieure en fonction de la sous-jacentes instructions du processeur, mais il est plus sûr à long terme. Je ne sais pas assez sur le fonctionnement interne de l' Thread.VolatileRead() ; Il peut être "mieux" au sujet de ce cas d'utilisation, car il offre encore plus de signatures.

Je ne voudrais pas essayer de lire directement la valeur (c'est à dire sans les mécanismes ci-dessus) au sein d'une boucle ou toute serrée appel de méthode, mais, depuis, même si les écritures sont volatiles et/ou barrière de mémoire d, rien n'est de dire au compilateur que la valeur du champ peut réellement changer entre les deux lectures. Ainsi, le champ doit être soit volatile ou de l'une de ces constructions doit être utilisé.

Mes deux cents.

8voto

user7116 Points 39829

Vous avez raison que vous n'avez pas besoin d'un enseignement spécial pour atomiquement lire un entier 32 bits, cependant, ce que cela signifie est que vous obtiendrez le "tout" de la valeur (c'est à dire que vous n'obtiendrez pas partie d'une écriture et d'une partie de l'autre). Vous n'avez aucune garantie que la valeur n'aura pas changé une fois que vous l'aurez lu.

C'est à ce point où vous devez décider si vous devez utiliser une autre méthode de synchronisation pour le contrôle d'accès, disons que si vous utilisez cette valeur pour lire un membre à partir d'un tableau, etc.


En un mot, l'atomicité assure une opération qui se passe complètement et de manière indivisible. Compte tenu de certaines opération A qui contenait N étapes, si vous l'avez fait pour le droit de l'opération après A vous pouvez être assuré que toutes N étapes qui s'est passé dans l'isolement d'opérations simultanées.

Si vous avez eu deux fils qui a exécuté l'opération atomique A vous garantis que vous verrez seulement les compléter résultat de l'un des deux fils. Si vous souhaitez coordonner les fils, les opérations atomiques pourraient être utilisés pour la création de la synchronisation. Mais les opérations atomiques dans et d'eux-mêmes ne pas assurer un haut niveau de synchronisation. L' Interlocked famille de méthodes sont disponibles pour fournir certains fondamentaux des opérations atomiques.

La synchronisation est une sorte de contrôle de concurrence, souvent construit autour atomique opérations. La plupart des processeurs mémoire obstacles qui vous permettent de s'assurer que toutes les lignes de cache sont vidées et vous avez un uniforme de vue de la mémoire. Volatile lit sont un moyen de s'assurer de la cohérence de l'accès à un emplacement mémoire.

Alors que pas immédiatement applicable à votre problème, à lire sur ACID (atomicité, cohérence, isolation et durabilité) à l'égard des bases de données peut vous aider avec la terminologie.

-7voto

Jay Points 4474

Oui, tout ce que vous avez lu est correct. Interlocked.Increment est conçu pour que les lectures normales ne soient pas fausses lors de la modification du champ. Lire un champ n'est pas dangereux, écrire un champ l'est.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X