7
votes

La classe verrouillée peut-elle être mélangée en toute sécurité avec serrure ()?

est une lecture atomique garantie lorsque vous mélangez des opérations verrouillées avec verrouillage () (et d'autres serrures de niveau supérieur)?

Je m'intéresse au comportement général lors de la mélange de mécanismes de verrouillage comme celui-ci, ainsi que des différences entre INT32 et INT64. xxx


6 commentaires

Ne verrouillez jamais Ceci , le verrouillage entrera en conflit avec un à l'extérieur de la classe de verrouillage de l'objet. Seulement jamais verrouillé sur des membres de type référence privé. Si vous n'avez pas de membre de type référence privé approprié du type correct VIS-A-Vis Static ou d'une instance, avez alors objet privé _mylock; ou objet statique privé _mysticlock; Juste à cette fin.


J'ai toujours le sentiment que vous pourriez avoir de plus gros poissons à frire que couvert dans cette question. Je suis content de ma réponse en ce qui concerne cette question, mais cela ne traite pas de questions de fil un fait sur la base du nombre de comptes tandis que le thread b change le compte, car c'est une question et quoi faire Si c'est le cas, cela dépend de plus que vous ne donnez ici.


@Jon - Je pense que le point de base est fait que mélanger ces métaphores est un champ de mines. Votre note sur l'utilisation de la valeur verrouillée mérite certainement un crédit supplémentaire, mais il n'y a pas de bouton '+2'. J'ai vraiment posé cette question à clarifier mon incertitude une réponse de mes propres ailleurs aujourd'hui.


C'est un champ de mines, bien que parfois nécessaire, comme je l'ai prouvé avec l'erreur (maintenant fixée) dans ma réponse ci-dessous dans l'oubli de la CPU CACHES (en fait, IIRC, il ne fera aucune différence sur une architecture X86, mais ce n'est pas quelque chose que vous devriez dépend de).


@Steve: Je me demandais si le véritable esprit de votre question serait mieux exprimé si votre exemple a utilisé un int64 au lieu de int32 . Il y a un interlocked.incrèce surcharge pour les champs 64 bits.


Ah! Maintenant, je dois changer ma réponse ...


5 Réponses :


3
votes

L'argument fourni au mot clé de verrouillage doit être un objet basé sur un type de référence. Dans votre code, il s'agit d'un type de valeur, cela signifie éventuellement la boxe, ce qui rend le relevé de verrouillage inutile.

Le fonctionnement verrouillé est atomique relativement à une autre opération verrouillée exécutée dans un autre fil et appliquée à la même variable. Il n'y a pas de sécurité de fil, si un thread utilise un fonctionnement verrouillé, et une autre modifie la même variable en utilisant un autre algorithme de synchronisation ou sans synchronisation.


0 commentaires

2
votes

La réponse à votre question est non. Le verrouillage et interclocké n'a rien à faire avec l'autre et ne fonctionne pas ensemble de la manière que vous suggérez.

En outre, je ne sais pas ce qui se passe avec votre code. Cela ne compile pas. Vous ne pouvez pas verrouiller un type de valeur. En outre, incrément () prend un argument ref .


2 commentaires

Ouais, ça va au moins compiler maintenant. Mais à coup sûr, ces deux types de serrures ne se synchronisent pas les uns avec les autres. En outre, vous ne devriez jamais verrouiller cela, mais c'est une discussion totalement différente.


Et maintenant que j'y pense, vous n'avez pas besoin du verrou sur la propriété. Le retour d'un Int est une opération atomique. Si tout est tout ce que vous avez, vous pouvez supprimer le verrou (ceci) et vous n'aurez pas de problème.



2
votes

Tout d'abord, vous ne pouvez pas verrouiller les types de valeur, comme int.

Lorsque vous verrouillez-vous sur une valeur de valeur, il est en premier lieu dans un objet en premier. Le problème est qu'il sera encadré à chaque fois et chaque fois que ce sera une "boîte" différente. Vous verrez à chaque fois sur un objet différent, rendant le bloc de verrouillage inutile.

Ce problème de côté, disons-vous que vous verrouillez un type de référence et vous utilisez la classe verrouillée sur un autre fil. Il n'y aura pas de synchronisation entre les threads. Lorsque vous souhaitez la synchronisation, vous devez utiliser le même mécanisme sur les deux threads.


0 commentaires

3
votes

est une lecture atomique garantie lorsque vous mélangez des opérations verrouillées avec une serrure () (et d'autres serrures de niveau supérieur)?

Une lecture atomique of int est toujours garantie quel que soit le verrouillage de n'importe quel type. La spécification C # indique que les lectures d'INT sont toujours atomiques.

Je pense que votre question actuelle est différente de celle que vous avez posée. Pouvez-vous clarifier la question?


1 commentaires

J'avais l'intention de poser une question plus générale sur le mélange de métaphores, édité en conséquence



9
votes

Note: Cette réponse (y compris les deux modifications déjà) a été donnée avant que la question ait été modifiée pour avoir un long compter . Pour la question actuelle au lieu de thread.volatileread J'utiliserais interlocked.Lead , qui a également une sémantique volatile et traiterait également le problème de lecture 64 bits discuté ici et introduit Dans la question

une lecture atomique est garantie sans verrouillage, car les lectures de valeurs correctement alignées de 32 bits ou moins, que votre comptent est garanti pour être atomique.

Ceci diffère d'une valeur de 64 bits où si elle démarré à -1, et s'il était lu alors qu'un autre fil l'incrémentait, pourrait entraîner la lecture de la valeur d'être -1 (arrivée avant incrément), 0 ( est arrivé après incrément) ou de 4294967295 ou de 4294967296 (32 bits écrites à 0, d'autres 32bits en attente d'écriture).

L'incrément atomique de intercrended.incremrement signifie que tout l'incrément L'opération est atomique. Considérez que l'incrément est conceptuellement:

  1. Lire la valeur.
  2. ajoutez-en une à la valeur.
  3. Écrivez la valeur.

    alors si x est 54 et un thread tente d'incrémenter alors que d'autres tentent de la définir à 67, les deux valeurs correctes possibles sont 67 (l'incrément se produit d'abord, puis est écrit. sur) ou 68 (l'affectation se produit d'abord, puis est incrémentée) mais l'incrément non atomique peut entraîner 55 (lecture incrémentée, attribution de 67, écrit incrément).

    Un cas réel plus courant est x est 54 et une incréments de fil et une autre diminution. Ici, le seul résultat valide est 54 (un en haut, puis une descente, ou vice-versa), mais sinon atomique, les résultats possibles sont des résultats de 53, 54 et 55.

    si vous voulez juste un compte Incrémenté atomiquement, le code correct est le suivant: xxx

    si vous souhaitez agir à ce sujet, il aura besoin de verrouillage plus fort. En effet, le fil utilisant le nombre peut devenir obsolète avant la fin de son fonctionnement. Dans ce cas, vous devez verrouiller tout ce qui se soucie du comte et de tout ce qui le change. Juste comment cela doit être fait (et si c'est même important à faire du tout) dépend de plus de questions de votre cas d'utilisation que d'être déduite de votre question.

    EDIT: Oh, vous voudrez peut-être verrouiller simplement pour forcer une barrière de mémoire. Vous voudrez peut-être aussi modifier la mise en oeuvre compter sur retour thread.volatileread (Ref Count); Pour vous assurer que les caches de la CPU sont rincées si vous allez retirer le verrouillage. Cela dépend de la manière dont la stéaleness cache est importante dans ce cas. (Une autre alternative consiste à faire compter volatile, comme tous les lisions et écrires seront volatils. Notez que cela n'est pas nécessaire pour les opérations en verrouillées car ils sont toujours volatils. )

    EDIT 2: En effet, vous voulez tellement de vouloir que cette lecture volatile, que je change la réponse ci-dessus. Il est possible que vous ne vous souciez pas de ce qu'il offre, mais beaucoup moins probable.


3 commentaires

Ne devrait pas compter être déclaré volatile dans ce cas? Sinon, vous vous retrouveriez avec des problèmes d'encaissement de registre, n'est-ce pas?


@Martin. GMTA a ajouté que comme vous avez commenté. Encore une fois, ce que nous voulons dépendra de quelques choses. Volatileread servira ce qui est dans la question, mais peut-être que volatile (selon votre suggestion) serait préférable une fois que d'autres opérations sont ajoutées. Il vaut la peine d'ajout de la complétude que l'API interclocké a les barrières de mémoire correctes que la volatilité n'est pas nécessaire avec eux (le dit même dans la documentation de l'avertissement que vous obtenez lorsque vous les appelez à la byref sur un champ volatil ).


Et félicitations à la personne demandant Stackoverflow.com/questions/3853678/... Depuis que je pensais à ma réponse à cela, que je réalisais cela aussi.