2010-06-05 14 views
5

Często widzę taki kod, który jest shown here, tj. Gdy obiekt jest przydzielany, a następnie używany jako "obiekt blokady".Blokowanie obiektu

Wydaje mi się, że można użyć dowolnego obiektu do tego, w tym samego zdarzenia, jako obiektu blokady. Po co przydzielać nowy obiekt, który nic nie robi? Rozumiem, że wywołanie funkcji lock() na obiekcie nie powoduje zmiany samego obiektu, ani też nie blokuje go przed użyciem, jest po prostu używane jako symbol zastępczy dla wielu instrukcji blokowania do zakotwiczenia.

public class Shape : IDrawingObject, IShape 
{ 
    // Create an event for each interface event 
    event EventHandler PreDrawEvent; 
    event EventHandler PostDrawEvent; 

    object objectLock = new Object(); 

    // Explicit interface implementation required. 
    // Associate IDrawingObject's event with 
    // PreDrawEvent 
    event EventHandler IDrawingObject.OnDraw 
    { 
     add 
     { 
      lock (objectLock) 
      { 
       PreDrawEvent += value; 
      } 
     } 
     remove 
     { 
      lock (objectLock) 
      { 
       PreDrawEvent -= value; 
      } 
     } 
    } 
} 

Moje pytanie brzmi: czy to naprawdę dobra rzecz?

+0

(patrz aktualizacja, btw) –

Odpowiedz

7

tym przypadku sama

Nie, nie możesz tego zrobić. "Zdarzenie" jest w rzeczywistości tylko niektórymi metodami dostępowymi. Zakładając, że masz na myśli delegata wspierającego, to byłoby bardzo źle - delegaci są niezmienni: za każdym razem, gdy dodajesz/usuwasz subskrybenta, dostaniesz innego delegata.

W rzeczywistości kompilator 4.0 robi to teraz z kodem bez blokady, używając Interlocked - może warto zamiast tego podążać za tym podejściem.

W swoim przykładzie objectLock zapewnia, że ​​wszystkie osoby dzwoniące (do tej instancji) są zabezpieczającą przed samym obiektu, co jest ważne - ale bez brzydoty blokowania na this (co jest jak kompilator C# używane pracować).

-

Aktualizacja: Twój przykład pokazuje kod, który jest niezbędny przed C# 4.0, dostęp do pola-jak-imprezy wewnątrz typ mówił bezpośrednio do pola: blokowanie normalne pole-jak-event nie szanowano. Zostało to zmienione w C# 4.0; można teraz (w języku C# 4.0) bezpiecznie re-write to jako:

public class Shape : IDrawingObject, IShape 
{ 
    // Create an event for each interface event 
    event EventHandler PreDrawEvent; 
    event EventHandler PostDrawEvent; 

    event EventHandler IDrawingObject.OnDraw 
    { 
     add { PreDrawEvent += value; } 
     remove { PreDrawEvent -= value; } 
    } 
} 

Wszystkie poprawne zachowanie jest następnie przestrzegane.

+0

Więc mówisz, że jeśli blokada (PreDrawEvent) spowodowałaby zablokowanie innego obiektu za każdym razem, gdy delegat został zmieniony? Ugh .. To nie wydaje się bardzo ... intuicyjne. Zawsze zakładałem, że wydarzenia były kontenerami, jak List <>, które zawierały delegatów. Czy to błędne założenie? –

+0

@Mystere Man - gorzej: niesubskrybowane wydarzenie ma wartość 'null', więc nie powiedzie się całkowicie. Twoje założenie jest nieprawidłowe. –

+1

Podejmowanie założeń dotyczących blokowania to naprawdę zły pomysł. Dokładna wiedza na temat działania zamka jest najważniejsza. – Rusty

1

Zaleca się blokowanie na prywatnym polu statycznym, ponieważ zapewnia to blokowanie wielu wątków próbujących uzyskać dostęp do blokady równolegle. Blokowanie instancji samej klasy (lock(this)) lub niektórych pól instancji może być problematyczne, ponieważ jeśli dwa wątki wywołają metodę w dwóch różnych instancjach obiektu, będą mogły jednocześnie wprowadzić instrukcję blokowania.

+0

Tak, ale to nie jest to, co powiedziałem. Miałem na myśli używanie prywatnego elementu zdarzenia jako zmiennej blokującej (tj. PreDrawEvent). Powinienem również zauważyć, że przykład nie używa zmiennej statycznej. Poza tym zablokowanie tego byłoby problematyczne, gdybyś miał więcej niż jedno oświadczenie blokujące w klasie. –

+0

To samo: blokowanie "tego" lub "pola instancji" może być złe, ponieważ pole instancji zmieni się w zależności od instancji obiektu, podczas gdy statyczne pole readonly nigdy się nie zmieni. –

+0

Naprawdę nie widzę problemu. Cały problem polega na zablokowaniu instancji zmiennej, aby wiele wątków nie mogło zmienić tej instancji. Dlaczego chcesz zapobiec jednoczesnemu modyfikowaniu dwóch różnych instancji? –

2

Dowolny prywatny członek typu odniesienia wykona zadanie. Dopóki jest to prywatne i nigdy nie zostanie ponownie przypisane. Który wyrzuca obiekt uczestnika z biegu, nie chcesz, aby blokada nie wykonała swojej pracy, ponieważ kod klienta, który nie kontrolujesz, przypisuje obsługę zdarzenia. Niezwykle trudny do debugowania.

Używanie prywatnych członków, którzy wykonują inną pracę, nie jest czymś, co dobrze się skaluje. Jeśli dowiesz się, że musisz zablokować inny region kodu podczas refaktoryzacji lub debugowania, musisz znaleźć innego członka prywatnego. Właśnie tam sprawy mogą się szybko skończyć: możesz ponownie wybrać tego samego członka prywatnego. Martwy puka do drzwi.

Nie dzieje się tak, jeśli obiekt blokady zostanie przypisany do określonego zestawu wspólnych zmiennych, które wymagają ochrony. Pozwala również nadać mu dobre imię.

+0

Dlatego właśnie zasugerował użycie zdarzenia, ponieważ był to "obiekt", który manipulowałeś. Jednak jeśli dobrze zrozumiem komentarze Marca, użycie zdarzenia jako obiektu blokującego jest bardzo złe ... –