W dobrze znanym Artykuł Josepha Albahari na temat gwintowania, wszystkie zmienne klas używane przez wiele wątków są deklarowane jako static
pola => wszystkie wątki mają dostęp do tej samej zmiennej. To musi być wyposażony przez lock()
mechanizm we wszystkich miejscach czytania / pisania i praca jest wykonywana.
Moje pytanie dotyczy implementacji właściwości klasy. Rozumiem, że jest to ważne, jeśli zaimplementuję (na przykład) właściwość Timeout
za pomocą static
zaplecze sklepu:
class MyClassWithWorkerThread {
private readonly object _locker = new object();
private static int _Timeout = false;
private int Timeout {
get {
lock (_locker) {
return _Timeout;
}
}
set {
lock (_locker) {
_Timeout = value;
}
}
}
}
To zmieni zmienną _Timeout
wspólne dla wszystkich instancji klasy.
Ale w moim przypadku przetwarzanie wielu wątków jest prywatne dla instancji klasy. Zaczyna się od New()
i kończy się na Dispose()
. Dostęp do głównego i roboczego wątku Timeout
właściwość (ale _Timeout
backing store nigdy nie jest dostępny poza getter / setter właściwości).
nie chcę _Timeout
wartość do zastosowania. Chcę to mieć unikalny dla każdej instancji klasy.
Moje pytanie brzmi: czy mogę bezpiecznie usunąć static
od _Timeout
zmienna, aby to osiągnąć?
Uwaga: przykro mi, jeśli w kodzie są jakieś błędy, faktycznie używam VB.NET i przekonwertowałem go za pomocą narzędzia. Mam nadzieję, że główne pytanie jest nadal jasne.
Odpowiedzi:
3 dla odpowiedzi № 1Absolutnie bezpieczne i dość zalecane (zmienne statyczne są bolesne przy testowaniu, nawet jeśli są potrzebne). Zakładając, że bezpieczny masz również na myśli ważny nie musisz zapominać, że:
this.Timeout = 0; // This is safe and valid
++this.Timeout; // This is safe but not valid
Bo ++
operator nie jest atomowy (dlatego mamy Interlocked
klasa). Oczywiście to samo dotyczy tej sytuacji:
if (this.Timeout == 0)
Timeout = 10;
Ponieważ nawet jeśli każdy dostęp jest bezpieczny (Powiedziałbym, że czytanie nieruchomości jest zawsze bezpieczne, ale możesz odczytać starą wartość bez lock
bariera) nie jest to operacja atomowa, a wartość może ulec zmianie po teście i przed nowym przypisaniem. Jeszcze bardziej skomplikowane?
if (this.Timeout == 0)
Timeout = Timeout * 2;
W takim przypadku za każdym razem, gdy czytasz Timeout
możesz otrzymać inną wartość. Z tego powodu powiedziałem, że blokada we właściwości jest rzadko przydatna, chyba że jest to właściwość tylko do odczytu. Znacznie lepiej usunąć tę blokadę z właściwości get / set i zawinąć kod w instrukcję lock:
lock (_locker) {
if (this.Timeout == 0)
Timeout = Timeout * 2;
}
Zauważ też, że dla int _Timeout
(Przyjmuję zadanie z false
to tylko literówka) możesz po prostu usunąć blokadę i zrobić to volatile
:
private volatile int _Timeout;
Oczywiście to nie rozwiąże innych opisanych problemów, ale może być użyteczne (i szybsze) dla właściwości tylko do odczytu (lub w ściśle kontrolowanych sytuacjach, volatile
modyfikatory mogą być trudne i mają inne znaczenie w porównaniu z C i łatwo o tym zapomnieć dostęp są atomowe, ale nic więcej).
1 dla odpowiedzi nr 2
Statyczne słowo kluczowe jest ortogonalnym (tzn. Niezależnym lub niezwiązanym z) wątkiem.
Słowa kluczowego static należy używać tylko wtedy, gdy chcesz tylko jedno wystąpienie tej właściwości / field / class / method / etc.
W twojej sytuacji nie potrzebujesz statycznego ustawienia _Timeout, jeśli chcesz, aby ta wartość była unikalna dla instancji.
Nie przeczytałem artykułu, do którego linkujesz, ale myślę, że chyba go źle zrozumiałeś (lub autor źle zrozumiał użycie statycznego)