2009-06-03 28 views
8

Każdy wie, że to nie jest bezpieczeństwo wątków:Czy C# "??" Czy wątek operatora jest bezpieczny?

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

Co na ten temat?

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

Specyfikacja C# ostrożnie określa, jakie operacje są atomowe; Operatory koalescencyjne zerowe nie są atomowe. Operator koalescencji zerowej jest tylko cukrem syntaktycznym dla twojego pierwszego kawałka kodu. Ale masz tu większe problemy; kogo to obchodzi, jeśli pole jest bezpieczne dla wątków? Konstruktor nie jest bezpieczny dla wątków! –

+4

W przypadku przyszłych pytań w tym temacie pomocne byłoby podanie dokładnej definicji dokładnie tego, co oznacza "bezpieczna dla nici". Bezpieczeństwo gwintów nie jest absolutne; raczej kod jest bezpieczny dla wątków, jeśli umowa użytkowania zaimplementowana przez wywołujących jest zgodna z oczekiwaną przez odbiorcę. Nie wiedząc, jakiego kontraktu oczekujesz, nie można powiedzieć, czy kod podąża za nim, czy nie. –

Odpowiedz

10

BEGIN EDYCJA

podstawie edytowanego tytułu, sam operator null koalescencyjny wydaje się być bezpieczny wątku (patrz Phil Haack's analysis). Wydaje się jednak, że nie gwarantuje to potencjalnym wielokrotnym wywołaniom konstruktora StringBuilder.

END EDIT

masz większy problem z gwintem, a to, że nieruchomość sama Builder reprezentuje stan, który może być współdzielona przez wątkach. Nawet jeśli sprawisz, że wątek inicjalizacji będzie bezpieczny, nie ma gwarancji, że metody zużywające Buildera robią to w sposób bezpieczny dla wątków.

// 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; 
    } 
} 

Powyższe spowoduje problem wątków w leniwej inicjalizacji _builder, ale chyba zsynchronizować połączenia metod instancji StringBuilder, nie masz zagwarantowane bezpieczeństwo wątków w dowolnych metod, które zużywają właściwość Builder. Wynika to z tego, że metody instancji w StringBuilder nie zostały zaprojektowane jako bezpieczne dla wątków. Zobacz poniższy tekst z MSDN StringBuilder page.

Wszystkie publiczne statyczny (Shared w języku Visual Basic) członków tego typu są bezpieczne wątek . Dowolne elementy instancji nie są gwarantowane jako bezpieczne dla wątków.

Jeśli spożywasz StringBuilder w wielu wątkach, prawdopodobnie lepiej jest ci go zamknąć w swojej klasie. Dokonaj Builder prywatne i odsłonić to, co trzeba zachowanie jako metoda publicznego:

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

W ten sposób nie piszesz kod synchronizacji w każdym miejscu.

+0

Po prostu myślałem, że? jest operacją atomową. ? nie jest bezpieczny dla wątków? –

+3

Nie mogę powiedzieć, czy operator koalescencji zerowej jest atomowy, ale moje twierdzenie jest takie, że masz większy problem, ponieważ StringBuilder nie jest samoistnie wątkowo bezpieczny. –

+1

Zobacz edytowaną odpowiedź, aby uzyskać odpowiedź na temat bezpieczeństwa wątków łączącego null (kredyt dla Phil Haacka). Jest bezpieczny w wątku, ponieważ nie tworzy warunków wyścigowych, ale potencjalnie może się skończyć dwoma osobnymi instancjami Buildera, jeśli warunki są idealne. –

10

że nie więcej lub mniej jest bezpieczny wątku; nadal możesz mieć dwa wątki sprawdzania wartości zerowej w tym samym czasie, tworząc oddzielne obiekty i nie widząc drugiego.

+0

Co sądzisz o użyciu Interlocked.CompareExchange (ref _builder, new StringBuilder(), null)? – LBushkin

2

Odpowiedzi udzielone są poprawne, oba są nie wątkowo. W rzeczywistości są one w większości równoważne, a operator ?? jest po prostu magią kompilatora, aby kod był szczuplejszy. Musisz użyć mechanizmu synchronizacji, jeśli chcesz, aby wątki były bezpieczne.

2

Nie testowałem tego zbliżyć się, ale jeśli chcesz bezpieczeństwa wątku bez obciążania systemu blokującego i nie martwisz się o potencjalnie tworzenia i odrzucając instancję obiektu, można spróbować to:

using System.Threading; 

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

Wywołanie CompareExchange() spowoduje atomową zamianę wartości w _builder na nowe wystąpienie StringBuilder tylko wtedy, gdy _builder == null.Wszystkie metody w klasie Interlocked są niedozwolone przez przełączniki wątków.

+0

BTW, to chyba zły pomysł, aby udostępnić instancję StringBuilder przez wątki. SB z natury nie jest bezpieczne dla wątków i nie jest jasne, nawet gdyby było, że można zrobić z nim coś sensownego w wątkach, które nie są zsynchronizowane. – LBushkin

Powiązane problemy