2009-05-28 8 views
6

W aplikacji C#, załóżmy, że mam jedną globalną klasę, która zawiera pewne elementy konfiguracji, tak jak poniżej:C bezpieczeństwo # wątek globalnych ustawień konfiguracyjnych

public class Options 
{ 
    int myConfigInt; 
    string myConfigString; 
    ..etc. 
} 

static Options GlobalOptions; 

członków tej klasy będą zastosowania w różnych wątków:

Thread1: GlobalOptions.myConfigString = blah; 

podczas

Thread2: string thingie = GlobalOptions.myConfigString; 

Używanie blokady dostępu do GlobalOptions obiekt byłby również niepotrzebny blokować, gdy 2 wątki mają dostęp do różnych elementów, ale z drugiej strony tworzenie obiektu synchronizacji dla każdego członka wydaje się również nieco przesadzone.

Ponadto użycie blokady w opcjach globalnych spowodowałoby, że mój kod byłby mniej ładny; jeśli muszę napisać

string stringiwanttouse; 
lock(GlobalOptions) 
{ 
    stringiwanttouse = GlobalOptions.myConfigString; 
} 

wszędzie (i jest to bezpieczny wątku lub jest stringiwanttouse teraz tylko wskaźnik do myConfigString? Tak, jestem nowy w C# ....) zamiast

string stringiwanttouse = GlobalOptions.myConfigString; 

powoduje, że kod wygląda okropnie.

Więc ... Jaki jest najlepszy (i najprostszy!) Sposób na zapewnienie bezpieczeństwa gwintów?

+0

Myślę, że byłoby jeszcze wyraźniej, gdybyś mógł określić, jaki problem próbujesz rozwiązać. Nie ma nieodłącznego niebezpieczeństwa w dwóch wątkach ustawiających wartość globalną w tym samym czasie. Środowisko CLR nie ulegnie awarii. Jakiego rodzaju problem z synchronizacją próbujesz rozwiązać? – PeterAllenWebb

+0

Co się dzieje, gdy jeden wątek zapisuje ciąg, a inny próbuje go odczytać? Czy zawsze otrzymam jeden lub drugi ciąg, czy może się zdarzyć, że otrzymam częściowo zastąpiony ciąg? – Led

Odpowiedz

4

Możesz zawinąć dane pole (myConfigString w tym przypadku) w usłudze i mieć kod w Get/Set, który używa albo Monitor.Lock albo Mutex. Następnie dostęp do właściwości blokuje tylko jedno pole i nie blokuje całej klasy.

Edycja: Kod dodanie

private static object obj = new object(); // only used for locking 
public static string MyConfigString { 
    get { 
     lock(obj) 
     { 
      return myConfigstring; 
     } 
    } 
    set { 
     lock(obj) 
     { 
      myConfigstring = value; 
     } 
    } 
} 
+0

+1 Nie musisz też blokować intów ani żadnych obiektów, które gwarantują operacje atomowe (tj. Bezpieczeństwo wątków). –

+0

ładny połów! thx Ed. – Mike

+0

Ed to niebezpieczne stwierdzenie. http://thith.blogspot.com/2005/11/c-interlocked.html – dss539

0

konfiguracje mogą być „globalny”, ale nie powinny one być wystawiony jako zmienną globalną. Jeśli konfiguracje się nie zmieniają, należy je wykorzystać do skonstruowania obiektów, które potrzebują tych informacji - ręcznie lub przez obiekt fabryczny. Jeśli zmienią się , należy użyć obiektu, który obserwuje plik konfiguracyjny/bazę danych/cokolwiek i implementuje Observer pattern.

zmienne globalne (nawet tych, które stało się wystąpienie klasy) są złe ™

+0

w tym przypadku jest to głównie ciąg znaków określający katalog pobierania, który może zostać zmieniony przez użytkownika w dowolnym momencie. Myślę, że wzorzec Observer może być przesadzony dla tylko 1 lub 2 ciągów globalnych? – Led

+0

Aby to rozwinąć, nie jest to kod produkcyjny. Wiem o etyce programowania;) To tylko małe narzędzie do projektowania hobby na własny użytek. – Led

3

Poniższa został napisany przed edytować OP:

public static class Options 
{ 
    private static int _myConfigInt; 
    private static string _myConfigString; 

    private static bool _initialized = false; 
    private static object _locker = new object(); 

    private static void InitializeIfNeeded() 
    { 
     if (!_initialized) { 
      lock (_locker) { 
       if (!_initialized) { 
        ReadConfiguration(); 
        _initalized = true; 
       } 
      } 
     } 
    } 

    private static void ReadConfiguration() { // ... } 

    public static int MyConfigInt { 
     get { 
      InitializeIfNeeded(); 
      return _myConfigInt; 
     } 
    } 

    public static string MyConfigString { 
     get { 
      InitializeIfNeeded(); 
      return _myConfigstring; 
     } 
    } 
    //..etc. 
} 

Po tej edycji Mogę powiedzieć, że powinieneś zrobić coś jak wyżej i ustawić konfigurację tylko w jednym miejscu - klasie konfiguracji. W ten sposób będzie to jedyna klasa modyfikująca konfigurację w środowisku wykonawczym i tylko wtedy, gdy ma zostać pobrana opcja konfiguracji.

+0

Nie sądzę, że próbuje zsynchronizować dostęp do swojego pliku konfiguracyjnego, który wygląda dokładnie tak, jak podałeś. Ponadto zapewniono dostęp tylko do odczytu. – dss539

+0

Celowo zrobiłem dostęp tylko do odczytu. To jest klasa konfiguracji. Jedyną rzeczą modyfikującą konfigurację powinna być klasa. –

+0

Mój błąd, przepraszam John, po ponownym przeczytaniu mojego pytania uznałem, że jest ono zbyt nieprecyzyjne, więc próbowałem trochę rozwinąć ... – Led

0

Co masz na myśli, mówiąc o bezpieczeństwie nici? To nie jest obiekt globalny, który musi być bezpieczny dla wątków, jest to kod dostępu. Jeśli dwa wątki napiszą do zmiennej składowej w pobliżu tej samej chwili, jedna z nich "wygra", ale czy to jest problem?Jeśli twój kod klienta zależy od tego, czy globalna wartość pozostaje stała, dopóki nie zostanie wykonana z jakąś jednostką przetwarzania, musisz utworzyć obiekt synchronizacji dla każdej właściwości, która musi być zablokowana. Nie ma na to świetnej okazji. Można tylko buforować lokalną kopię wartości, aby uniknąć problemów, ale zastosowanie tej poprawki zależy od okoliczności. Ponadto, nie utworzyłbym obiektu synchronizacji dla każdej właściwości domyślnie, ale zamiast tego, gdy zdasz sobie sprawę, że go potrzebujesz.

Powiązane problemy