[EDIT]Abgeteilt von Frage zu Threads und Locks[/EDIT]
NIE lock(this) verwenden ... das kann zu Deadlocks führen.
Wieso, weshalb, warum, siehe: http://www.des-eisbaeren-blog.de/Blog.aspx?Permalink=lockthis
Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden
Hallo Golo
NIE lock(this) verwenden ... das kann zu Deadlocks führen.
ich sehe das wie schon früher diskutiert wesentlich entspannter: Entwurfsmuster Singleton und Schlüsselwort synchronized [und sollte lock(this) verwendet werden?] und folgende Beiträge.
herbivore
Suchhilfe: 1000 Worte, lock, this
ich sehe das wie schon früher diskutiert wesentlich entspannter:
Naja, schon aus Kapselungssicht, gehört lock(this) verboten. Jeder Nutzer eines Objektes kann mittels lock(obj) schöne Deadlocks erzeugen.
Bestenfalls kann man lock(htis) innerhalb von privaten/nested Klassen durchgehen lassen.
Angesichts der Tatsache, dass es praktisch Null Mehraufwand bedeutet, ein explizites Sperrobjekt zu erzeugen, kann man zu lock(this) eigentlich nur sagen: Pfui!
Hallo svenson,
diese Argumentation habe ich schon im anderen Thread widerlegt, in dem Sinne, dass man mit und ohne lock (this) gleichermaßen Deadlocks erzeugen oder eben vermeiden kann.
herbivore
diese Argumentation habe ich schon im anderen Thread widerlegt
Ich kann deine Argumentation nicht nachvollziehen. Wenn ich eine Klasse habe, die sauber thread-safe implementiert ist, dann kannst du von außen lock() aufrufen wie du willst, du erzeugst keinen Deadlock innerhalb der Klasse. Bei einer lock(this)-Implementierung ist es dagegen möglich:
Und schon haben wir einen Deadlock.
Natürlich kann man argumentieren, dass ein lock(obj) bei einer thread-safe Klasse nicht nötig ist. Aber das Kapselungsprinzip soll ja auch und gerade möglichen Nutzungsfehlern vorbeugen. Wenn ich es in der Hand habe, eine Klasse "sicher" zu machen, dann tue ich es.
Hallo svenson,
du meinst doch, dass bei allen lock Operationen nur das eine Sperrobjekt (nämlich das Objekt this) verwendet wird. Dann gibt es in dem von dir beschriebenen Fall keinen Deadlock. Innerhalb eines Threads kann dasselbe Objekt mehrfach gelockt werden, ohne dass der Thread wartet.
Insofern ist meine Argumentation weiterhin nicht widerlegt 🙂
herbivore
du meinst doch, dass bei allen lock Operationen nur das eine Sperrobjekt (nämlich das Objekt this)
Nein, da hast du mich missverstanden. Der Konsument sperrt direkt das verwendete Objekt. Das Objekt intern mit this. Wenn die Klasse anstelle von lock(this) ein explizites Sperrobjekt verwendet, hat der Konsument keine Möglichkeit das Verhalten des Objektes zu beeinflussen. Das was man von Kapselung erwartet.
Hallo svenson,
Nein, da hast du mich missverstanden. Der Konsument sperrt direkt das verwendete Objekt. Das Objekt intern mit this.
dann ist das doch aber dasselbe Objekt. Dass es unter verschiedenen Variablennamen angesprochen wird, macht keinen Unterschied. Wichtig ist nur das Objekt, die verwendete Variable spielt keine Rolle. Es gibt also keinen Deadlock.
herbivore
Innerhalb eines Threads kann dasselbe Objekt mehrfach gelockt werden, ohne dass der Thread wartet.
Ah, das hatte ich überlesen. Aber nun ist es doch gerade so, dass in Fällen, wo gelockt wird, mehrere Threads im Spiel sind....
Hallo svenson, hallo Golo,
Aber nun ist es doch gerade so, dass in Fällen, wo gelockt wird, mehrere Threads im Spiel sind....
selbstverständlich. Ich habe ja auch nur den Grund geschrieben, warum es in deinem Beispiel, svenson, keinen Deadlock gibt.
Kommen wir mal zu harten Fakten. In dem folgenden Programm kommt es zu einem Deadlock, wenn man - wie von MS und euch empfohlen - ein separates Sperrobjekt objSync verwendet. Wenn man jedoch - ohne andere Änderungen am Code - das Objekt selbst (also this) sperrt, kommt es zu keinem Deadlock.
Natürlich ist es immer Sache des Programmierers, der eine Klasse benutzt, einen Deadlock zu vermeiden und insofern ist das Programm natürlich falsch geschrieben. Aber es ging ja in Golos und deiner Argumentation auch darum, dass die Verwendung eines separaten Sperrobjekts robuster ist und man dadurch Nutzungsfehlern vorbeugen kann. Wie man sieht, ist das Gegenteil der Fall!
Aber seht selbst. Erstmal der Code:
using System;
using System.Threading;
public class Deadlock
{
Object objSync = new Object ();
public void Method () {
Console.WriteLine ("{0,-4}: Begin Method (WAITING for objSync)", Thread.CurrentThread.Name);
lock (this) { // hier objSync durch this ersetzen und der Deadlock verschwindet.
Console.WriteLine ("{0,-4}: objSync LOCKED", Thread.CurrentThread.Name);
// ...
OnMethodCalled (EventArgs.Empty);
// ...
}
Console.WriteLine ("{0,-4}: End Method (objSync RELEASED)", Thread.CurrentThread.Name);
}
public event EventHandler MethodCalled;
protected void OnMethodCalled (EventArgs e)
{
Console.WriteLine ("{0,-4}: Begin OnMethodCalled", Thread.CurrentThread.Name);
if (MethodCalled != null) {
MethodCalled (this, e);
}
Console.WriteLine ("{0,-4}: End OnMethodCalled", Thread.CurrentThread.Name);
}
}
static class App
{
private static Deadlock _dl;
public static void Main (string [] astrArg)
{
Thread.CurrentThread.Name = "Main";
Console.WriteLine ("{0,-4}: Begin Main", Thread.CurrentThread.Name);
new Thread (Run).Start ();
_dl = new Deadlock ();
_dl.MethodCalled += RunMethodCalled;
Console.WriteLine ("{0,-4}: In Main (WAITING for _dl)", Thread.CurrentThread.Name);
lock (_dl) {
Console.WriteLine ("{0,-4}: _dl LOCKED", Thread.CurrentThread.Name);
// ...
Thread.Sleep (1000);
_dl.Method ();
// ...
}
Console.WriteLine ("{0,-4}: End Main (_dl RELEASED)", Thread.CurrentThread.Name);
}
public static void Run ()
{
Thread.CurrentThread.Name = "Run";
Console.WriteLine ("{0,-4}: Begin Run", Thread.CurrentThread.Name);
Thread.Sleep (500);
// ...
_dl.Method ();
Console.WriteLine ("{0,-4}: End Run", Thread.CurrentThread.Name);
}
public static void RunMethodCalled (Object objSender, EventArgs e)
{
//...
Console.WriteLine ("{0,-4}: Begin RunMethodCalled", Thread.CurrentThread.Name);
Thread.Sleep (2000);
Console.WriteLine ("{0,-4}: In RunMethodCalled (WAITING for _dl)", Thread.CurrentThread.Name);
lock (_dl) {
Console.WriteLine ("{0,-4}: In RunMethodCalled (_dl LOCKED)", Thread.CurrentThread.Name);
Console.WriteLine ("{0,-4}: _dl", Thread.CurrentThread.Name);
//...
}
Console.WriteLine ("{0,-4}: End RunMethodCalled (_dl RELEASED)", Thread.CurrentThread.Name);
}
}
Die Ausgabe bei Verwendung von objSync:
Main: Begin Main
Main: In Main (WAITING for _dl) Main: _dl LOCKED Run : Begin Run Run : Begin Method (WAITING for objSync) Run : objSync LOCKED Run : Begin OnMethodCalled Run : Begin RunMethodCalled Main: Begin Method (WAITING for objSync) Run : In RunMethodCalled (WAITING for _dl)
Hier habe ich das Programm wegen des Deadlocks abgebrochen!
Wie man sieht, hat Main _dl gelockt und wartet auf objSync und Run hat objSync gelockt und wartet auf _dl. Der klassische Deadlock.
Die Ausgabe bei Verwendung von this:
Main: Begin Main
Main: In Main (WAITING for _dl)
Main: _dl LOCKED
Run : Begin Run
Run : Begin Method (WAITING for this)
Main: Begin Method (WAITING for this)
Main: this LOCKED
Main: Begin OnMethodCalled
Main: Begin RunMethodCalled
Main: In RunMethodCalled (WAITING for _dl)
Main: In RunMethodCalled (_dl LOCKED)
Main: _dl
Main: End RunMethodCalled (_dl RELEASED)
Main: End OnMethodCalled
Main: End Method (this RELEASED)
Main: End Main (_dl RELEASED)
Run : this LOCKED
Run : Begin OnMethodCalled
Run : Begin RunMethodCalled
Run : In RunMethodCalled (WAITING for _dl)
Run : In RunMethodCalled (_dl LOCKED)
Run : _dl
Run : End RunMethodCalled (_dl RELEASED)
Run : End OnMethodCalled
Run : End Method (this RELEASED)
Run : End Run
Also wunderbar durchgelaufen und dabei sauber synchronisiert. Erst läuft alles im Main durch (weil Run weiter oben auf das schon gesperrte Objekt wartet) und wenn im Main das Objekt wieder freigegeben ist, kommt Run dran.
NIE lock(this) verwenden ... das kann zu Deadlocks führen.
Gerade wenn man lock(this) verwendet, werden Deadlocks unwahrscheinlicher.
herbivore
PS: Weiter unten gibt es ein weiteres Beispiel, das trotz privater lock-Objekte zu einem Deadlock führen kann/wird, ohne das Events im Spiel sind.
Hallo Golo, hallo svenson,
es würde mich schon interessieren, was ihr dazu sagt.
herbivore
es würde mich schon interessieren, was ihr dazu sagt.
Ich sehe einen klaren Synchronisierungfehler, der nur nicht auftritt, weil this verwendet wird. Ein weiteres Beispiel, welches mich in meiner Meinung gegen lock/this) bestärkt.
Aber entscheidend ist - ich zitiere mal Brad Adams:
MSDN Blogs > Brad Abrams > Minor threading update
Do not lock on any public types or other instances you do not control. Notice the common construct:
lock (this) violates this guideline if this is publicly accessible.
Ms schreibt selbst:
Chapter 5 - Improving Managed Code Performance - Threading Guidelines
Locking and Synchronization Guidelines
This section summarizes guidelines to consider when developing multithreaded code that requires locks and synchronization:
* Acquire locks late and release them early. * Avoid locking and synchronization unless required. * Use granular locks to reduce contention. * Avoid excessive fine-grained locks. * Avoid making thread safety the default for your type. * Use the fine-grained lock (C#) statement instead of Synchronized. **\* Avoid locking "this".** * Coordinate multiple readers and single writers by using ReaderWriterLock instead of lock. * Do not lock the type of the objects to provide synchronized access.
Hallo svenson,
dann hast du, sorry, schlich und einfach falsch geschaut. Wenn lock this verwendet wird, tritt der Synchronisierungsfehler nicht auf!
Außerdem habe ich ja geschrieben, dass es ein klarer Synchroniserungsfehler ist. Du selbst hast aber gerade mit der Robustheit gegen Synchroniserungsfehler argumentiert. Die Robustheit ist in meinem Beispiel aber mit lock this besser.
Das alle möglichen Stellen schreiben, dass man es lassen soll, ist für mich kein Argument, solange mein Beispiel sagt, dass es andersherum besser ist.
herbivore
Die Robustheit ist in meinem Beispiel aber mit lock this besser.
Robustheit bedeutet in meinen Augen nicht, dass immanente Fehler durch Seiteneffekte nicht auftreten, sondern dass man keine Fehler provozieren kann. Sie bedeutet für mich, dass das Parallelitätsverhalten einer Klasse voll und ganz ihrer Kontrolle unterliegt. Das ist weder bei lock(this), noch in deinem Szenario der Fall.
Hallo svenson,
sondern dass man keine Fehler provozieren kann.
genau das habe ich aber gekonnt. Also kann man mindestens festhalten, dass die Empfehlung nicht davor schützt.
Sie bedeutet für mich, dass das Parallelitätsverhalten einer Klasse voll und ganz ihrer Kontrolle unterliegt.
Mein Szenario würde ebenfalls einen Deadlock produzieren, wenn
private static Deadlock _dl
und lock (_dl)
durch
private static Object objSync2
und lock (objSync2)
ersetzt werden würde. Dann wäre das eingehalten, was du sagt (dass das Parallelitätsverhalten einer Klasse voll und ganz ihrer Kontrolle unterliegt) und trotzdem würde ein Deadlock auftreten. Ohne diese Änderungen tritt der Fehler sogar, weil das Parallelitätsverhalten einer Klasse voll und ganz ihrer Kontrolle unterliegt.
Ich würde aus dem ganzen den Schluss ziehen, dass die Empfehlung (mindestens) nichts hilft.
Es gibt nur eine Möglichkeit, Deadlocks zu vermeiden: gründlich nachdenken.
herbivore
genau das habe ich aber gekonnt. Also kann man mindestens festhalten, dass die Empfehlung nicht davor schützt.
Nein, die Klasse selbst war bei der Verwendung des Sync-Objektes sauber implementiert. Man konnte dort keine Deadlocks innerhalb der Klasse durch Sperren von außen erzeugen.
Hallo zusammen,
ich kenne eigentlich auch nur die Prämisse, dass "lock(this)" nicht zu empfehlen ist. Gut, ich habe mir so detailliert noch keine Gedanken darüber gemacht, jedoch würde ich ähnlich wie Svenson argumentieren, dass ich aus Kapselungssicht heraus lieber ein Privates Lock-Objekt verwende, da es von außen nicht manipuliert werden kann.
Dass Dein Beispiel, Herbivore, jetzt mit einem privaten Lock-Objekt zu einem DeadLock führt, und dass lock(this) das ganze am Laufen hält ist für mich jetzt kein Widerlegungsbeispiel, sondern absichtlich so implementiert.
Ich kann Herbivore aber nur bekräftigen in der Aussage:
Es gibt nur eine Möglichkeit, Deadlocks zu vermeiden: gründlich nachdenken.
, dann würden private Lock-Objekte NIE zu einem DeadLock führen, was ich bei lock(this) so nicht behaupten würde??
Gruß
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Hallo svenson,
Man konnte dort keine Deadlocks innerhalb der Klasse durch Sperren von außen erzeugen.
naja, genau das habe ich in meinem Beispiel geschafft. 🙂 Immerhin ist an dem Deadlock das interne, gekappselte Sperrobjekt beteiligt.
Aber trotzdem verstehe ich jetzt, was du meinst. Mit einem gekapselten Sperrobjekt kann kein Benutzer der Klasse hingehen und durch direktes Sperren dieses Objekts einen Deadlock auslösen, weil er ja eben gar nicht an das Objekt herankommt.
Wie man aber an meinem Beispiel sieht, verhindert das keine Deadlocks. Selbst wenn die Klasse in sich sauber mit gekapselten Sperrobjekt implementiert ist, denn ein direkter Zugriff auf das gekapselte Sperrobjekt ist gar nicht nötig, um es an einem Deadlock zu beteiligen.
Hallo norman_timo,
dann würden private Lock-Objekte NIE zu einem DeadLock führen, was ich bei lock(this) so nicht behaupten würde??
so habe ich es gerade nicht gemeint. Man kann, ohne gründlich nachzudenken, sowohl mit lock (this) als auch mit gekapselten Sperrobjekten versehentlich einen Deadlock verursachen.
Das ist ja gerade, was mich am Sinn der Festlegung zweifeln lässt.
Hallo zusammen,
schön wäre ja mal ein Beispiel, bei dem die Verwendung von gekapselten Sperrobjekten einen Deadlock verhindern würde, der entstände, wenn man stattdessen lock (this) verwenden würde.
Oder bei dem sich zumindest ein anderer konkreter Vorteil offenbaren würde.
herbivore
Hallo Herbivore,
so habe ich es gerade nicht gemeint. Man kann, ohne gründlich nachzudenken, sowohl mit lock (this) als auch mit gekapselten Sperrobjekten versehentlich einen Deadlock verursachen.
Das weiß ich, aber ich glaube Du denkst da doch zu sehr egozentrisch 😉 Es gilt ja so stabil zu programmieren, dass auch kein anderer Programmierer versehentlich einen Deadlock erzeugen kann. Mit einem privaten Objekt glaube ich, dass das vorbeugend genug ist, was bei einem "lock(this)" nicht so trivial ist, und Kollegen dann wirklich von außen genau schauen müssten was sie tun.
Ich probier mal ein Deadlock Beispiel, was das demonstrieren würde, kann aber nicht versprechen, ob ich es zeitig hinbekomme...
Grüße
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Hallo norman_timo,
Es gilt ja so stabil zu programmieren, dass auch kein anderer Programmierer versehentlich einen Deadlock erzeugen kann
darum geht es auch in meinen Augen die ganze Zeit. Die Frage ist eben genau, ob das Vermeiden von lock (this) dabei hilft. Und ich habe ja sogar schon einen Fall gezeigt, wo gerade durch Vermeiden von lock (this) die Robustheit sinkt. Ein privates Objekt ist insofern also nicht vorbeugend genug, sondern kann eben sogar die Situation verschlechtern.
herbivore
Aber trotzdem verstehe ich jetzt, was du meinst. Mit einem gekapselten Sperrobjekt kann kein Benutzer der Klasse hingehen und durch direktes Sperren dieses Objekts einen Deadlock auslösen, weil er ja eben gar nicht an das Objekt herankommt.
Das ist - denke ich - der Kern des Ganzen. Entscheidend ist, die Klasse gegen Fehlbenutzung abzusichern. Mehr kann man eh nicht tun.
Hallo svenson,
nun zeigt aber mein Beispiel gerade, dass die vermeintliche Absicherung auch nach hinten losgehen kann. Sie produziert in einem bestimmte Szenario einen Deadlock, der nicht auftritt, wenn man die Empfehlung nicht einhält. Insofern ist schon die Frage gestattet (und darum geht es zumindest mir im Kern von Anfang an), ob diese Empfehlung sinnvoll ist. Uneingeschränkt sinnvoll ist sie, wie mein Beispiel zeigt sicher nicht.
Diese Empfehlung bringt in einigen Fällen einen Nutzen, in einigen Fällen einen Schaden.
herbivore
nun zeigt aber mein Beispiel gerade, dass die vermeintliche Absicherung auch nach hinten losgehen kann.
Ich sehe das anders. Die "Absicherung" führt zu definiertem und erwartetem Systemverhalten und das ist in diesem Fall ein Deadlock.
Stelle dir mal vor, du baust ein lock(this)-Konstrukt, dann noch einen zusätzlichen Fehler wie in dem von dir geposteten Beispiel und alles läuft scheinbar (!) sauber. Dann nimmt jemand später mal die Klasse erneut in die Hand, entdeckt die Stelle und baut - gemäß der MS-Guidelines - die Stelle um (wohlgemerkt alles lokal innerhalb der Klasse!). Schon knallt es - und zwar völlig woanders. Ich kann darin keinen Vorteil erkennen, sondern nur schwer wartbaren Code.
Hallo svenson,
wenn man sich gegen die Anwendung der Empfehlung entscheidet, dann kommt auch nachher keiner, der den Code so ändert, dass er der Empfehlung genügt. Man hat also genauso wenig ein Wartungsproblem, als wenn man die Empfehlung von vornherein einhält.
Sich dagegen zu entscheiden, wäre also kein Problem. Es wäre nur ein Problem, sich nicht zu entscheiden.
herbivore
hallo zusammen,
hier ein interessantes beispiel dazu aus dem buch "CLR via C#" von J. Richter.
class MyClass
{
Object syncObj = new object();
~MyClass()
{
lock (this)
{
}
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
Monitor.Enter(mc);
mc = null;
// Garbage Collection erzwingen
GC.Collect();
GC.WaitForPendingFinalizers();
Console.Write("Hier kommen wir nur an bei lock(syncObj) in ~MyClass");
Console.ReadKey();
}
}
ich finde das demonstriert eigentlich recht gut, warum man besser auf lock(this) verzichten sollte.
Hallo feadur,
ich bin zwar ein Kollege der Nicht-This These, aber das Beispiel ist schon sehr ungewöhnlich.
Wer kommt denn auf die Idee überhaupt einen Destruktur in .Net zu implementieren? Und dass es da zu komischen Effekten kommen kann, wenn man auf Klasseninformationen zugreift, das sollte klar sein.
Ich würde allgemein die Aussage unterstreichen, dass eben das Objekt "this" von außen manipulierbar ist, von mir aus auch durch Zerstörung, und dann ein "this" eben zu Threading-Deadlocks führt, jedoch würde ich es nie so wie in dem Beispiel zeigen.
Was hat der Autor sonst noch dazu geschrieben?
Grüße
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
hallo norman_timo,
es soll, wie du schon sagtest, nur die Problematik aufzeigen, dass man mit Monitor.Enter (bzw. lock) in den Besitz einer Sperre kommen kann (in Main).
Der Teil mit der Finalizer Methode dient nur zu Demonstrationszwecken.
Da der GarbageCollector in einem eigenen Thread operiert und von dort aus auf die Finalizer Methode zugreift, befindet dieser sich in einem Deadlock. Denn in Main wurde ja die Sperre für mc zuvor schon vom Hauptthread der Anwendung übernommen.
Deshalb hat dieses Beispiel eigentlich nichts mit der Frage nach Sinn und Unsinn von Destruktoren zutun.
Gruß
Ich würde vom Destruktor sowieso die Finger lassen, es sei denn es geht gar nicht anders. Die Implementierung eines Destruktors führt zu vielen unschönen Effekten und Problemen.
Es gibt nur zwei echte Vorteile gegenüber Dispose:
* sie kommen auf jeden Fall dran (man kann sie nicht vergessen)
* der Zeitpunkt des Aufrufes entspricht tatsächlich dem Wegräumen
In all den Jahren der .NET-Entwicklung habe ich nicht einmal ein Problem gehabt, dass ich nicht mit IDisposable hätte lösen können...
Das Lustige an dem Beispiel ist eigentlich nur, dass es den GC verklemmt.
Hallo feadur,
wo ich gerade noch mal auf den Thread gestoßen bin:
Das Beispiel ist auf jeden Fall ohne praktische Relevanz konstruiert, nicht nur, weil man - wie die anderen sagen - mit Destruktoren und erst recht mit lock in Destruktoren sehr zurückhaltend sein sollte, sondern schon alleine weil man in einem Destruktor nicht auf verwaltete Ressourcen - und damit nicht auf syncObj - zugreifen darf. Diese können bereits durch den CG zerstört sein. Die genannte Alternative, lock (syncObject)
zu verwenden, besteht also streng genommen gar nicht. Wenn überhaupt ist das Beispiel also ein Argument, warum man lock (this)
verwenden sollten, denn wenn ein lock im Destruktor nötig ist, ist das die einzige zulässige Möglichkeit.
Darüberhinaus muss man innerhalb eines locks (oder was das gleiche ist, nachdem ein Monitor.Enter ohne das zugehörige Monitor.Exit ausgeführt wurde) natürlich genau darauf achten, was man tut. Und wenn man dort ein GC.Collect und noch dazu ein GC.WaitForPendingFinalizers aufruft, obwohl man weiß, dass davon das synchronisierte Objekt betroffen ist (oder auch nur sein kann), das man gelockt hat, ist das schon mehr als fahrlässig.
Hallo zusammen,
und wo ich gerade dabei bin, hier noch mal ein wichtiges Argument aus dem anderen Thread (Entwurfsmuster Singleton und Schlüsselwort synchronized), das für lock (this)
spricht.
Nehmen wir beispielsweise eine Collection
coll
, die internlock (this)
verwendet. Wenn nun ein Benutzer von außerhalblock (coll)
verwendet, um z.B. ein for-Schleife über die Collection zu sperren, sperrt die Collection intern mit demselben Objekt, wie der Benutzer der Collection für die Schleife. Einen Konflikt gibt es deswegen noch lange nicht. Es ist eher im Gegenteil genau das was man will, dass eben andere Operationen auf der Collection von anderen Threads nicht ausgeführt werden können, solange die Schleife läuft.
Im Ergebnis heißt das für mich: wenn man nicht aufpasst, kann man mit lock (privateSyncObject)
genauso viel falsch machen wie mit lock (this)
, aber mit lock (this)
gibt man dem Benutzer des Klasse mehr Möglichkeiten und mehr Flexibilität an die Hand.
herbivore
Suchhilfe: 1000 Worte, lock, this
Hallo zusammen,
aus aktuellem Anlass grabe ich den Thread nochmal aus, weil mir ein weiteres Beispiel eingefallen ist, bei dem es trotz Verwendung privater lock-Objekte zu einem Deadlock kommen kann.
Denn privat bedeutet ja nicht, dass nur auf das lock-Objekt der eigenen Instanz zugegriffen werden kann. Sondern aus einer Instanz heraus sind Zugriffe auf alle lock-Objekte aller dort bekannten Instanzen möglich.
Zum Beispiel könnte es für die Thread-Sicherheit erforderlich sein, in der Equals-Methode einer Klasse beide zu vergleichenden Objekte vor dem Vergleich über ihr jeweiliges lock-Objekt zu sperren. Wenn der Implementierer der Methode public bool Equals(Object obj)
erst lock (this.privateSyncObject)
und dann lock (obj.privateSyncObject)
ausführt, kann - und wird es bei entsprechendem Timing - zu einem Deadlock kommen, wenn der der Benutzer der Klasse aus dem einen Thread a.Equals(b)
und aus einem anderen Thread b.Equals(a)
aufruft. Analog natürlich für operator==
oder alle anderen Operationen und Methoden, bei der zwei oder mehr Objekte derselben Klasse gesperrt werden.
Man sieht auch hier wieder: private lock-Objekte schützen nicht automatisch vor Deadlocks, sondern es steht und fällt damit, dass der Implementierer aufpasst.
Oder wie ich es im vorigen Beitrag ausgedrückt habe: Wenn man nicht aufpasst, kann man mit lock (privateSyncObject)
genauso viel falsch machen wie mit lock (this)
, aber mit lock (this)
gibt man dem Benutzer des Klasse mehr Möglichkeiten und mehr Flexibilität an die Hand.
herbivore