Pourquoi est-lock(this) {...} mauvais?
La La documentation MSDN dit que
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
est "un problème si l'instance est accessible au public". Je me demande pourquoi? Est-ce parce que la serrure se tiendra plus longtemps que nécessaire? Ou est-il une plus insidieux raison?
Vous devez vous connecter pour publier un commentaire.
C'est mauvais pour utiliser
this
dans les instructions lock parce qu'il est généralement hors de votre contrôle qui d'autre pourrait être de verrouillage sur cet objet.Afin de planifier correctement les opérations en parallèle, des précautions particulières doivent être prises afin d'envisager d'éventuelles situations de blocage, et d'avoir un nombre inconnu de verrouiller les points d'entrée qui empêche ce. Par exemple, l'une avec une référence à l'objet peut se verrouiller sur elle, sans que l'objet concepteur/créateur de savoir à ce sujet. Cela augmente la complexité de multi-thread solutions et peut nuire à leur exactitude.
Un champ privé est généralement une meilleure option que le compilateur va appliquer des restrictions d'accès, et il va encapsuler le mécanisme de verrouillage. À l'aide de
this
viole l'encapsulation en exposant une partie de votre verrouillage de la mise en œuvre au public. Il n'est pas aussi clair que vous fera l'acquisition d'un verrou surthis
, sauf si elle a été documentée. Même puis, en s'appuyant sur la documentation de prévenir un problème est sous-optimale.Enfin, il existe une idée fausse commune que
lock(this)
modifie en fait l'objet passé en paramètre, et d'une certaine façon fait en lecture seule ou inaccessibles. C'est faux. L'objet passé en paramètre àlock
sert seulement à clé. Si un verrou est déjà en cours à cette clé, la serrure ne peut pas être fait; sinon, la serrure est autorisé.C'est pourquoi c'est mauvais d'utiliser les chaînes de caractères comme les clés dans
lock
états, car ils sont immuables et sont partagés/accessible à l'ensemble des parties de l'application. Vous devez utiliser une variable privée au lieu de cela, unObject
instance fera très bien l'affaire.Exécuter le code C# suivant comme exemple.
Sortie de la Console
lock(this)
est conseillé de le faire; il est important de noter que cela fera généralement qu'il est impossible pour l'extérieur code de provoquer le verrouillage associés à l'objet à être tenue au cours entre les appels de méthode. Cela peut ou peut ne pas être une bonne chose. Il y a danger en permettant à l'extérieur de code à placer un verrou pour arbitraire, la durée, et les classes doivent généralement être conçus de façon à en faire un tel usage est inutile, mais il n'y a pas toujours de solutions pratiques. Comme exemple simple, à moins d'une collection met en œuvre unToArray
ouToList
de la méthode de son propre...there is the common misconception that lock(this) actually modifies the object passed as a parameter, and in some way makes it read-only or inaccessible. This is false
- Je crois que ces pourparlers sont sur SyncBlock peu dans le CLR objet de manière formelle ce qui est juste - le verrouillage de l'objet modifié lui-mêmeCar si les gens peuvent obtenir auprès de votre instance de l'objet (c'est à dire: votre
this
) pointeur, alors ils peuvent également essayer de verrouiller ce même objet. Maintenant, ils peuvent ne pas être conscient que vous êtes le verrouillage dethis
en interne, ce qui peut causer des problèmes (peut-être une impasse)En plus de cela, c'est aussi une mauvaise pratique, car c'est le verrouillage "trop"
Par exemple, vous pourriez avoir une variable de membre de
List<int>
, et la seule chose que vous avez réellement besoin de verrou est que la variable de membre. Si vous verrouillez l'ensemble de l'objet dans vos fonctions, puis d'autres choses qui appeler ces fonctions seront bloqués en attente de la serrure. Si ces fonctions n'ont pas besoin d'accéder à la liste des membres, vous allez être à l'origine d'un autre code d'attendre et de ralentir votre application pour aucune raison du tout.Prendre un coup d'oeil à la Rubrique MSDN La Synchronisation Des Threads (Guide De Programmation C#)
Je sais que c'est un vieux thread, mais parce que les gens peuvent encore faire des recherches et de compter sur elle, il semble important de souligner que
lock(typeof(SomeObject))
est bien pire quelock(this)
. Ayant dit cela; sincère bravo à Alan pour préciser quelock(typeof(SomeObject))
est une mauvaise pratique.Une instance de
System.Type
est l'un des plus générique, à grain grossier, objets, il est. À tout le moins, une instance de Système.Le Type est global à un domaine d'application, et .NET peut exécuter plusieurs programmes dans un domaine d'application. Cela signifie que les deux programmes totalement différents pourrait potentiellement causer de l'interférence de l'un à l'autre, voire à la création d'un blocage si ils ont tous les deux essayer d'obtenir un verrou de synchronisation sur le même type d'instance.Donc
lock(this)
n'est pas particulièrement robuste, peut causer des problèmes et il faut toujours remonter les sourcils, pour toutes les raisons citées. Pourtant, il est largement utilisé, relativement bien respecté et apparemment stable, un code comme log4net qui utilise la serrure(ce) modèle largement, même si je préfère personnellement de voir que le changement de motif.Mais
lock(typeof(SomeObject))
ouvre une toute nouvelle et améliorée de la boîte de pandore.Pour ce que ça vaut.
...et exactement les mêmes arguments s'appliquent à cette construction ainsi:
lock(this)
semble très logique et concise. C'est terriblement grossier de verrouillage, et tout autre code pourrait prendre un verrou sur votre objet, ce qui peut causer des interférences dans votre code interne. Prendre plus granulaire des serrures, et d'assumer plus de contrôle. Celock(this)
ne va bien pour elle, c'est que c'est beaucoup mieux quelock(typeof(SomeObject))
.Imaginez que vous avez un métier de secrétaire au bureau c'est une ressource partagée dans le département. Une fois dans un certain temps, vous vous précipitez vers eux parce que vous avez une tâche, seulement à espérer qu'une autre de vos collègues n'a pas déjà demandé pour eux. Habituellement, vous n'avez qu'à attendre pour une brève période de temps.
Parce que prendre soin de vous pour le partage, votre gestionnaire décide que les clients peuvent utiliser le secrétaire directement. Mais cela a un effet secondaire: Un client peut même prétendre pendant que vous travaillez pour ce client et vous avez aussi besoin d'eux pour l'exécution d'une partie des tâches. Un blocage se produit, car en prétendant n'est plus une hiérarchie. Cela aurait pu être évité tous ensemble en ne permettant pas aux clients de les faire valoir, en premier lieu.
lock(this)
est mauvais comme nous l'avons vu. Un objet extérieur peut se verrouiller sur l'objet et puisque vous n'avez pas le contrôle qui est à l'aide de la classe, n'importe qui peut se verrouiller sur elle... ce Qui est exactement l'exemple décrit ci-dessus. Encore une fois, la solution est de limiter l'exposition de l'objet. Toutefois, si vous avez unprivate
,protected
ouinternal
classe que vous pourrait déjà de contrôle qui est le verrouillage de votre objet, parce que vous êtes sûr que vous avez écrit votre code vous-même. Donc, ici, le message est: ne pas exposer commepublic
. Aussi, s'assurer qu'un verrou est utilisé dans les mêmes d'un scénario évite les blocages.Le contraire de ce est de se verrouiller sur les ressources qui sont partagées dans tout le domaine de l'application -- le pire des cas. C'est comme mettre votre secrétaire à l'extérieur et permettant à tout le monde de les revendiquer. Le résultat est un pur chaos, ou en termes de code source: il était une mauvaise idée, loin de le jeter et de recommencer. Alors, comment faisons-nous cela?
Types de communes dans le domaine de l'application comme la plupart des gens ici le point de sortir. Mais il y a quand même d'autres choses que nous pouvons utiliser: les chaînes de caractères. La raison en est que les chaînes sont mis en commun. En d'autres termes: si vous avez deux chaînes qui ont le même contenu dans un domaine d'application, il y a une chance qu'ils ont exactement le même pointeur. Depuis le pointeur est utilisé comme clé de verrouillage, ce que vous obtiendrez est un synonyme de "préparer un comportement indéfini".
De même, vous ne devriez pas verrouiller sur WCF objets, HttpContext.Actuel, Fil.Actuel, les Singletons (en général), etc. La façon la plus simple pour éviter tout cela?
private [static] object myLock = new object();
De verrouillage sur le ce pointeur peut être mauvais si vous êtes de verrouillage sur une ressource partagée. Une ressource partagée peut être une variable statique ou un fichier sur votre ordinateur, c'est à dire quelque chose qui est partagé entre tous les utilisateurs de la classe. La raison en est que le pointeur this contiendra une référence différente pour un emplacement en mémoire à chaque fois que votre classe est instanciée. Donc, le verrouillage sur ce à la fois instance d'une classe est différente de verrouillage sur ce dans une autre instance d'une classe.
Découvrez ce code pour voir ce que je veux dire. Ajoutez le code suivant à votre programme principal dans une application Console:
Créer une nouvelle classe comme ci-dessous.
Ici est un parcours du programme de verrouillage sur ce.
Ici est un parcours du programme de verrouillage sur myLock.
Il est très bon article sur le sujet http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects par Rico Mariani, la performance de l'architecte pour Microsoft® .NET runtime
Extrait:
Il y a aussi quelques bonnes discussion à ce sujet ici: Est-ce la bonne utilisation d'un mutex?
Ici est beaucoup plus simple illustration (prises de Question 34 ici) pourquoi verrouillage(ce) est mauvais et peut entraîner des blocages lors de la consommation de votre classe aussi essayer de verrou sur l'objet.
Ci-dessous, une seule des trois thread peut se poursuivre, les deux autres sont dans l'impasse.
À contourner, ce mec utilisé du Fil.TryMonitor (avec délai) à la place de la serrure:
https://blogs.appbeat.io/post/c-how-to-lock-without-deadlocks
SomeClass
, je reçois toujours le même blocage. Aussi, si le verrou dans la classe principale est fait sur un autre privé, membre de l'instance de Programme, le même verrou se produit. Donc, vous ne savez pas si cette réponse n'est pas trompeuse et erronée. Voir que les comportements ici: dotnetfiddle.net/DMrU5hParce que tout morceau de code qui peut voir l'instance de votre classe peut également verrouiller sur cette référence. Vous souhaitez cacher (encapsuler) votre objet de verrouillage, de sorte que seul code, qui doit faire référence, il peut faire référence à elle. Le mot-clé this fait référence à la classe en cours d'instance, de sorte que n'importe quel nombre de choses pourrait faire référence à celui-ci et pourrait l'utiliser pour faire la synchronisation des threads.
Pour être clair, ce est mauvais parce qu'une autre partie du code peut utiliser l'instance de classe pour verrouiller, et pourrait empêcher votre code à partir de l'obtention en temps opportun d'une serrure ou pourrait créer d'autres thread des problèmes de synchronisation. Dans le meilleur des cas: rien d'autre utilise une référence à votre classe de verrouillage. Carrure: quelque chose utilise une référence à votre classe pour faire des verrous et il provoque des problèmes de performances. Le pire des cas: quelque chose utilise une référence de votre classe pour faire des verrous et il provoque vraiment mauvais, vraiment subtil, vraiment difficile à déboguer des problèmes.
Voici un exemple de code qui est plus simple à suivre (OMI): Volonté de travailler dans LinqPad, référence espaces de noms suivants: System.Net et le Système.Le filetage.Tâches)
DoWorkUsingThisLock
fil n'est pas nécessaire pour illustrer le problème?Veuillez consulter le lien suivant qui explique pourquoi de verrouillage (ce) n'est pas une bonne idée.
http://blogs.msdn.com/b/bclteam/archive/2004/01/20/60719.aspx
Donc la solution est d'ajouter un objet privé, par exemple, la zone lockobject à la classe et le lieu de la région de code à l'intérieur de l'instruction verrouillage comme indiqué ci-dessous:
Désolé les gars mais je ne peux pas d'accord avec l'argument que le verrouillage cela pourrait provoquer un blocage. Vous confondez deux choses: d'interblocage et de la faim.
Ici est un tableau qui illustre la différence.
Conclusion
Vous pouvez toujours l'utiliser en toute sécurité
lock(this)
si l'insuffisance de thread n'est pas un problème pour vous. Vous devez toujours garder à l'esprit que lorsque le thread, qui est affamé thread à l'aide delock(this)
se termine dans une serrure avoir votre objet est verrouillé, il sera enfin la fin éternelle faim 😉lock(this)
- ce genre de code est tout simplement faux. Je pense juste que l'appelant de l'interblocage est un peu abusif.Vous pouvez établir une règle qui dit qu'une classe peut avoir des code que les verrous sur les " il " ou tout objet dont le code dans la classe instancie. Donc, c'est seulement un problème si le motif n'est pas suivie.
Si vous voulez vous protéger de code qui ne suivent pas ce modèle, l'on a accepté la réponse est correcte. Mais si la tendance est suivie, il n'est pas un problème.
L'avantage de verrouillage(ce) est l'efficacité. Que faire si vous avez un simple "objet de valeur" qui détient une valeur unique. C'est juste un wrapper, et il est instanciée des millions de fois. En exigeant la création d'un privé de synchronisation objet juste pour le verrouillage, vous avez doublé la taille de l'objet et a doublé le nombre d'allocations. Lorsque la performance est importante, c'est un avantage.
Lorsque vous n'avez pas de soins sur le nombre d'allocations ou de la mémoire, en évitant de verrouillage(ce) est préférable pour les raisons indiquées dans d'autres réponses.
Il y aura un problème si l'instance est accessible au public, car il pourrait y avoir d'autres demandes qui pourraient être à l'aide de la même instance d'objet. Il est préférable d'utiliser privée/variable statique.