2010-05-12 11 views
5

Napisałem, co mam nadzieję, jest lekką alternatywą dla używania klas ManualResetEvent i AutoResetEvent w C#/.NET. Powodem tego było posiadanie funkcjonalności typu Event bez wagi użycia obiektu blokującego jądro.Lekka alternatywa dla Manual/AutoResetEvent w C#

Mimo że kod wydaje się działać dobrze zarówno w testowaniu, jak i produkcji, uzyskanie tego wszystkiego w sposób właściwy dla wszystkich możliwości może być dużym przedsięwzięciem, a ja pokornie zażądam wszelkich konstruktywnych komentarzy i komentarzy od użytkowników StackOverflow na ten temat. Mam nadzieję, że (po przeglądzie) będzie to przydatne dla innych.

Użycie powinno być podobne do klas Manual/AutoResetEvent z funkcją Notify() używaną do Set().

Tu idzie:

using System; 
using System.Threading; 

public class Signal 
{ 
    private readonly object _lock = new object(); 
    private readonly bool _autoResetSignal; 
    private bool _notified; 

    public Signal() 
    : this(false, false) 
    { 
    } 

    public Signal(bool initialState, bool autoReset) 
    { 
    _autoResetSignal = autoReset; 
    _notified = initialState; 
    } 

    public virtual void Notify() 
    { 
    lock (_lock) 
    { 
     // first time? 
     if (!_notified) 
     { 
     // set the flag 
     _notified = true; 

     // unblock a thread which is waiting on this signal 
     Monitor.Pulse(_lock); 
     } 
    } 
    } 

    public void Wait() 
    { 
    Wait(Timeout.Infinite); 
    } 

    public virtual bool Wait(int milliseconds) 
    { 
    lock (_lock) 
    { 
     bool ret = true; 
     // this check needs to be inside the lock otherwise you can get nailed 
     // with a race condition where the notify thread sets the flag AFTER 
     // the waiting thread has checked it and acquires the lock and does the 
     // pulse before the Monitor.Wait below - when this happens the caller 
     // will wait forever as he "just missed" the only pulse which is ever 
     // going to happen 
     if (!_notified) 
     { 
     ret = Monitor.Wait(_lock, milliseconds); 
     } 

     if (_autoResetSignal) 
     { 
     _notified = false; 
     } 
     return (ret); 
    } 
    } 
} 
+1

byłaś porównywana to przed użyciem ManualResetEvent/AutoResetEvents? Jaka jest różnica w wydajności? – James

+0

Nie, jeszcze nie tak, ponieważ głównym celem było uczynienie obsługi/zasobu bez jądra za pomocą obiektu zdarzenia. Spróbuję jednak przygotować kilka testów, dzięki. – sweetlilmre

+1

Wdrażanie własnych prymitywów wątków jest jak wdrażanie własnych algorytmów kryptograficznych. Jeśli nie jesteś ekspertem w dziedzinie, możesz to * zepsuć. Nawet jeśli * jesteś * ekspertem, nadal * możesz * zepsuć to. Nie rób tego. .NET 4 i tak ma już "lekkie" wersje, 'ManualResetEventSlim' i podobne klasy. – Aaronaught

Odpowiedz

4

Działa to z założenia, że ​​wydarzenia Win32 są drogie. Nie są, niewiele mogę sobie wyobrazić, że jest to tańsze niż wydarzenie. Główną wskazówką, że tak właśnie jest, są projektanci .NET decydujący, że dobrym pomysłem byłoby użycie zdarzenia Win32 do implementacji MRE i ARE.

Prawdziwym kosztem zamiennika jest największy FUD, jaki odczujesz, kiedy masz wyścig do gwintowania i nie wiesz, co go powoduje.

+1

Założeniem jest, że ARE i MRE używają zdarzenia jądra pod maską, które byłoby droższe niż monitor. Chciałbym być udowodniony źle :) – sweetlilmre

+0

Ten artykuł przez microsoft szczegóły, które są szybkie: http://msdn.microsoft.com/en-us/library/ms228964.aspx –

+0

Z punktu widzenia przejścia trybu jądra w .NET, zdarzenia Win32 są kosztowne, stąd lekkie dodatki w .NET 4, jak na @Aaronaught odpowiedź powyżej. – sweetlilmre

1

Niestety, poprawna implementacja monitora jest dość ciężka, biorąc pod uwagę prymitywy synchronizacji Win32. Moim początkowym podejrzeniem jest to, że "blokada" byłaby większa w korzystaniu z zasobów niż zdarzenie (i prawdopodobnie jest zbudowana na szczycie zdarzenia).

+0

Jestem prawie pewien, że blokada nie jest w żaden sposób realizowana przez wydarzenie pod maską. IIRC są specjalne struktury wątku używane specjalnie do obsługi blokowania wątków i uczynienia tego bardziej lekkim niż zdarzenia. – sweetlilmre

+0

@sweetlilmre - Sprawdziłem implementację Rotor/SharedCCI i używają niektórych blokad typu spinlock, ale w razie potrzeby sprowadza się to do typu zdarzenia dostarczanego przez hosta. –

+0

Tak, obracają się, używając własnych bitów synchronizacji. Jedną rzeczą, której nie grokowałem, jest to, jak poznają program planujący system Windows. Masz kod loc? –

1

Jednym ze sposobów optymalizacji wydajności AutoResetEvent jest utrzymanie jego stanu (sygnalizowanego/niesygnalizowanego) we własnej zmiennej, więc przed wykonaniem podróży do jądra i rzeczywistym wykorzystaniem obiektu zdarzenia, można po prostu sprawdzić stan aplikacji zmienne i pozostają przez cały czas w trybie użytkownika.
Wysłałem demonstration tej koncepcji kilka miesięcy temu.