2009-06-03 7 views
8

Jeder weiß, dass dies nicht sicher ist Thread:Ist das C# '??' Bediener Thread sicher?

public StringBuilder Builder 
{ 
    get 
    { 
     if (_builder != null) 
      _builder = new StringBuilder(); 
     return _builder; 
    } 
} 

Was ist das?

public StringBuilder Builder 
{ 
    get { return _builder ?? (_builder = new StringBuilder()); } 
} 
+13

Die C# -Spezifikation nennt sorgfältig, welche Operationen atomar sind; Null-Koaleszenzoperatoren sind nicht atomar. Der Koaleszenzoperator null ist nur ein syntaktischer Zucker für Ihren ersten Codeabschnitt. Aber Sie haben hier größere Probleme; Wen interessiert es, wenn das Feld threadsicher ist? Der Erbauer ist nicht threadsicher! –

+4

Für zukünftige Fragen in diesem Sinne wäre es hilfreich, wenn Sie eine genau definierte Definition von genau was "Thread Safe" für Sie bedeutet. Fadensicherheit ist kein absolutes; Code ist threadsicher, wenn der von den Aufrufern implementierte Nutzungsvertrag mit dem vom Angerufenen erwarteten Vertrag kompatibel ist. Ohne zu wissen, welchen Vertrag Sie erwarten, ist es unmöglich zu sagen, ob der Code dem folgt oder nicht. –

Antwort

10

EDIT

Basierend auf Ihrem bearbeiteten Titel BEGIN, scheint der Null-Koaleszenz-Operator selbst Thread-sicher (siehe Phil Haack's analysis) zu sein. Es scheint jedoch, dass es nicht gegen die möglichen mehrfache Aufrufe des StringBuilder-Konstruktors garantiert.

END EDIT

Sie haben ein größeres Problem mit einem Gewinde, und das ist, dass die Builder-Eigenschaft selbst Zustand darstellt, die über Threads gemeinsam genutzt werden können. Selbst wenn Sie den Thread für die verzögerte Initialisierung sicher machen, gibt es keine Garantie dafür, dass Methoden, die den Builder verwenden, threadsicher arbeiten.

// below code makes the getter thread safe 
private object builderConstructionSynch = new object(); 
public StringBuilder Builder 
{ 
    get 
    { 
     lock (builderConstructionSynch) 
     { 
      if (_builder == null) _builder = new StringBuilder(); 
     } 
     return _builder; 
    } 
} 

oben Das wird das Einfädeln Problem in der verzögerte Initialisierung von _builder, verhindern aber, wenn Sie Ihre Anrufe zu Instanzmethoden String synchronisieren, sind Sie nicht Thread-Sicherheit in allen Methoden garantiert, dass die Builder Eigenschaft verbrauchen. Dies liegt daran, dass Instanzmethoden in StringBuilder nicht threadsicher sind. Siehe den folgenden Text von MSDN StringBuilder page.

Alle öffentlichen static (Shared in Visual Basic) Member dieses Typs sind thread sicher. Alle Instanzmitglieder sind nicht garantiert Thread-sicher.

Wenn Sie StringBuilder in mehreren Threads verwenden, ist es wahrscheinlich besser, sie in Ihrer Klasse zu kapseln. Machen Builder private und belichten, welches Verhalten Sie sich als öffentliche Methode benötigen:

public void AppendString(string toAppend) 
{ 
    lock (Builder) 
    { 
     Builder.Append(toAppend); 
    } 
} 

Auf diese Weise Sie nicht Synchronisationscode der ganzen Ort zu schreiben.

+0

Ich dachte nur, dass ?? ist atomare Operation. ? ist nicht auch thread sicher? –

+3

Ich kann nicht sagen, ob der nullverschmelzende Operator atomar ist oder nicht, aber meine Behauptung ist, dass Sie ein größeres Problem haben, da StringBuilder nicht an sich threadsicher ist. –

+1

Siehe bearbeitete Antwort zur Antwort über Thread-Sicherheit des Null-Coalescing-Operators (Kredit an Phil Haack). Es ist insofern threadsicher, als es keine Race Conditions erzeugt, aber Sie könnten möglicherweise mit zwei separaten Instanzen von Builder enden, wenn die Bedingungen perfekt sind. –

8

NO für beide Versionen

10

dass nicht mehr oder weniger threadsicher; Sie könnten immer noch zwei Threads haben, die den Null-Check gleichzeitig durchführen, also separate Objekte erstellen und das andere nicht sehen.

+0

Was ist Ihre Meinung zur Verwendung von Interlocked.CompareExchange (ref _builder, new StringBuilder(), null)? – LBushkin

2

Die gegebenen Antworten richtig sind, sind beide nicht thread. Tatsächlich sind sie meistens äquivalent und der Operator ?? ist nur Compiler-Magie, um den Code schlanker zu machen. Sie müssen einen Synchronisationsmechanismus verwenden, wenn Sie möchten, dass dies threadsicher wird.

2

Ich habe dies selbst nicht getestet nähern, aber wenn Sie Thread-Sicherheit, ohne den Overhead eines Sperrschema wollen, und Sie sind nicht besorgt über potenziell zu schaffen und eine Objektinstanz zu verwerfen, könnten Sie dies versuchen:

using System.Threading; 

public StringBuilder Builder 
{ 
    get 
    { 
     if (_builder != null) 
      Interlocked.CompareExchange(ref _builder, new StringBuilder(), null); 
     return _builder; 
    } 
} 

Der Aufruf von CompareExchange() führt nur bei _builder == null eine atomare Ersetzung des Werts in _builder durch eine neue Instanz von StringBuilder durch.Alle Methoden in der Interlocked-Klasse sind so ausgelegt, dass sie NICHT durch Thread-Switches vorweggenommen werden.

+0

BTW, ist es wahrscheinlich eine schlechte Idee, eine Instanz eines StringBuilder über Threads zu teilen. SB ist inhärent nicht threadsicher, und es ist unklar, selbst wenn es so wäre, dass Sie über Threads, die nicht synchronisiert sind, etwas Sinnvolles damit machen könnten. – LBushkin