2012-04-01 1 views
5

Angenommen ich ein Objekt A haben, enthaltendSperrbereich in C#: Ist das zurückgegebene Objekt immer noch "gesperrt"?

// ... 

private List<double> someList = new List<double>(); 

// ... 

public List<double> SomeList 
{ 
    get { lock (this) { return someList; } } 
} 

// ... 

wäre es threadsicher sein, den Betrieb auf der Liste, wie in der nachfolgenden Code auszuführen. Wissend, dass mehrere Operationen gleichzeitig von verschiedenen Threads ausgeführt werden können.

A.SomeList.Add(2.0); 

oder

A.SomeList.RemoveAt(0); 

Mit anderen Worten, wenn die Verriegelung gelöst?

+5

Sie sollten niemals ein Objekt sperren, das außerhalb Ihrer Klasse sichtbar ist, z. B. 'this'. – svick

+0

Ok, fair genug. Angenommen, ich sperre ein Objekt ein, das nicht nach außen hin frei ist. –

+0

@svick warum nicht? – surfen

Antwort

5

Das Schloss, das Sie in der Frage gezeigt haben, ist nicht von großem Nutzen.

Um Listenoperationen threadsicher zu machen, müssen Sie Ihre eigenen Add/Remove/etc-Methoden implementieren, die die der Liste umschließen.

public void Add(double item) 
{ 
    lock(_list) 
    { 
     _list.Add(item); 
    } 
} 

Es ist auch eine gute Idee, die Liste selbst vor den Verbrauchern Ihrer Klasse zu verbergen, d. H. Das Feld privat zu machen.

+0

Danke das war, was ich fürchtete. –

+2

Sie können nicht 'SyncRoot' auf einer' Liste 'so zugreifen, weil es eine explizite Schnittstellenimplementierung ist. Und ich denke, es gibt keinen guten Grund, es zu benutzen, es ist dort meistens nur für Rückwärtskompatibilität. – svick

+0

@svick: Danke. Korrigiert. – Gebb

12

Hier gibt es keine Threadsicherheit.

Die lock wird, sobald der Block freigegeben es, kurz vor dem Grundstück zurückkehrt, so dass die Anrufe Add ad RemoveAt sind nicht geschützt durch das Schloss ist abgeschlossen schützt.

+0

Danke das, was ich though. Gibt es eine übliche Möglichkeit, eine Situation wie diesen Thread sicher zu machen? –

+0

@ user1162647 - Ja, Sie sperren die _operations_ für das gemeinsame Objekt, das Sie schützen möchten. – Oded

2

Die Sperre wird aufgehoben, wenn der Code innerhalb der Sperre beendet ist. Diese Sperre wirkt sich nur auf die aktuelle Instanz des Objekts aus

3

Die Sperre wird aufgehoben, wenn Sie den Textkörper der lock-Anweisung verlassen. Das bedeutet, dass Ihr Code nicht thread-safe ist.

Mit anderen Worten, Sie können sicher sein, dass zwei Threads nicht return someList gleichzeitig auf demselben Objekt ausgeführt werden. Aber es ist sicherlich möglich, dass ein Thread Add() gleichzeitig ausführt, während ein anderer Thread RemoveAt() ausführt, was es nicht Thread-sicher macht.

1

Ok, nur zum Teufel.
Es gibt eine Möglichkeit, Ihr Objekt threadsafe zu machen, indem Sie Architekturen verwenden, die bereits threadsafe sind.

Zum Beispiel könnten Sie Ihr Objekt ein einzelnes Gewinde COM Objekt machen. Das COM-Objekt wird threadsicher sein, aber Sie zahlen mit der Leistung (der Preis dafür, dass Sie faul sind und Ihre eigenen Sperren nicht verwalten).

Create a COM Object in C#

+0

Das ist wie eine Fliege mit einer Lokomotive zu töten. Es könnte tatsächlich funktionieren, aber es ist schwieriger, es richtig zu machen und kann Nebenwirkungen verursachen, die Sie später beißen könnten. – svick

0

... andere schon gesagt, aber nur ein Problem ein wenig zu formalisieren ...

  • Zuerst schlägt lock (this) {...} ein 'Umfang' - z.B. wie using(){} - es sperrt nur (das in diesem Fall) für Variablen innerhalb.Und das ist eine "gute Sache" :) eigentlich, als ob man sich nicht darauf verlassen könnte, dass das ganze Locks/Synchronisationskonzept sehr nutzlos wäre,
  • lock (this) { return something; } ist ein Oxymoron einer Art - es gibt etwas zurück, das die sehr entriegelt Im selben Moment, es gibt
  • Und die Probleme, die ich denke, ist das Verständnis, wie es funktioniert. 'lock()' wird in einem Zustand des Objekts nicht "beibehalten", so dass Sie es usw. zurückgeben können. Schauen Sie hier, wie es implementiert wird How does lock work exactly? - Antwort erklärt es. Es ist eher ein "kritischer Abschnitt" - d. H. Sie schützen bestimmte Teile von "Code", der die Variable verwendet - nicht die Variable selbst. Jede Art der Synchronisation erfordert 'Synchronisationsobjekte', um die Sperren zu halten - und die entsorgt werden müssen, sobald die Sperre nicht mehr benötigt wird. Werfen Sie einen Blick auf diesem Beitrag https://stackoverflow.com/a/251668/417747, formuliert Esteban, dass es sehr gut,

„Schließlich gibt es das weit verbreitete Missverständnis ist, dass sperrt (dies) tatsächlich das als Parameter übergebene Objekt modifiziert, und in gewisser Weise macht es schreibgeschützt oder nicht zugegriffen werden. Dies ist falsch. Das Objekt als Parameter übergeben lediglich sperren dient als Schlüssel“ (dies ist ein Zitat)

  • Sie entweder (in der Regel) sperren‚privaten Code‘ einer Klassenmethode, Eigenschaft ... - um den Zugriff auf etwas zu synchronisieren Du machst das innerhalb - und erreichst den Zugang zu einem privaten Mitglied (wieder normalerweise), so dass niemand sonst darauf zugreifen kann, ohne deinen synchronisierten Code durchzugehen.
  • Oder Sie erstellen eine thread-sichere 'Struktur' - wie eine Liste - die bereits 'synchronisiert' ist, so dass Sie thread-sicher darauf zugreifen können. Aber es gibt keine solchen Dinge (oder nicht verwendet, fast nie) als vorübergehende Sperren um oder einen Ort sperren die Variable, während der andere Teil des Codes es entsperrt usw. (in diesem Fall ist es eine Art von EventWaitHandle, die eher verwendet wird synchronisieren Dinge dazwischen ‚ferner‘ Code, wo man auf einer anderen usw. feuert)
  • in Ihrem Fall ist die Wahl ich mit der ‚synchronisierten Struktur‘ zu gehen, denken, dh die Liste, die intern behandelt hat,