Stell dir vor, du hast eine erfahrene Sekretärin in deinem Büro, die eine gemeinsam genutzte Ressource in der Abteilung ist. Ab und zu stürmst du auf sie zu, weil du eine Aufgabe hast, nur um zu hoffen, dass sich nicht schon einer deiner Kollegen ihrer bedient hat. Normalerweise musst du nur eine kurze Zeit warten.
Weil Fürsorge gleichbedeutend ist mit Teilen, entscheidet dein Manager, dass Kunden die Sekretärin auch direkt nutzen können. Aber das hat eine Nebenwirkung: Ein Kunde könnte sie sogar beanspruchen, während du gerade für diesen Kunden arbeitest und sie auch benötigst, um einen Teil der Aufgaben auszuführen. Es kommt zu einer Sackgasse, weil die Beanspruchung hierarchisch nicht mehr geordnet ist. Dies hätte von Anfang an vermieden werden können, indem man Kunden nicht erlaubt hätte, sie zu beanspruchen.
lock(this)
ist schlecht, wie wir gesehen haben. Ein externes Objekt könnte sich auf das Objekt sperren und da du nicht kontrollierst, wer die Klasse nutzt, kann sich jeder darauf sperren... Genau wie im obigen Beispiel beschrieben. Auch hier ist die Lösung, die Exposition des Objekts zu begrenzen. Wenn du jedoch eine private
, protected
oder internal
Klasse hast, könntest du bereits kontrollieren, wer sich auf dein Objekt sperrt, weil du sicher bist, dass du deinen Code selbst geschrieben hast. Die Botschaft hier lautet also: stelle es nicht als public
zur Verfügung. Auch die Verwendung eines Sperrmechanismus in ähnlichen Szenarien vermeidet Sackgassen.
Das genaue Gegenteil davon ist das Sperren von Ressourcen, die im gesamten Anwendungsdomäne gemeinsam genutzt werden - der schlimmste Fall. Das wäre so, als ob du deine Sekretärin nach draußen stellst und es jedem erlaubst, sie zu beanspruchen. Das Ergebnis ist völliges Chaos - oder in Bezug auf den Quellcode: es war eine schlechte Idee; wirf sie weg und fange von vorne an. Wie machen wir das also?
Typen werden in der Anwendungsdomäne geteilt, wie die meisten hier betonen. Aber es gibt sogar bessere Dinge, die wir nutzen können: Strings. Der Grund dafür ist, dass Strings gepoolt sind. Mit anderen Worten: Wenn du in einer Anwendungsdomäne zwei Strings hast, die den gleichen Inhalt haben, besteht die Möglichkeit, dass sie den genau gleichen Zeiger haben. Da der Zeiger als Sperrschlüssel verwendet wird, bekommst du im Grunde genommen ein Synonym für "Bereite dich auf undefiniertes Verhalten vor".
Ähnlich solltest du dich nicht auf WCF-Objekte, HttpContext.Current, Thread.Current, Singletons (allgemein) usw. sperren. Der einfachste Weg, all dem auszuweichen? private [static] object myLock = new object();