2010-05-12 11 views
5

He escrito lo que espero sea una alternativa ligera al uso de las clases ManualResetEvent y AutoResetEvent en C# /. NET. El razonamiento detrás de esto era tener un evento como la funcionalidad sin el peso de usar un objeto de bloqueo del kernel.Alternativa ligera a Manual/AutoResetEvent en C#

Aunque el código parece funcionar bien tanto en pruebas como en producción, conseguir este tipo de cosas correctas para todas las posibilidades puede ser una tarea tensa y humildemente solicito cualquier comentario constructivo y/o crítica del público de StackOverflow sobre esto. Con suerte (después de la revisión) esto será útil para otros.

El uso debe ser similar a las clases Manual/AutoResetEvent con Notify() utilizado para Set().

aquí va:

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

¿Usted como punto de referencia contra el uso de este ManualResetEvent/AutoResetEvents? ¿Qué tan significativa es la diferencia de rendimiento? – James

+0

No, aún no he tenido el objetivo principal de crear un recurso/manejador que no sea kernel utilizando un objeto de evento. Aunque intentaré hacer algunas pruebas, gracias. – sweetlilmre

+1

La implementación de sus propias primitivas de subprocesamiento es como implementar sus propios algoritmos de criptografía. A menos que sea un experto en el dominio, * lo * arruinará. Incluso si * eres * un experto, todavía * podrías * arruinarlo. No lo hagas .NET 4 ya tiene versiones "ligeras" de todos modos, 'ManualResetEventSlim' y clases relacionadas. – Aaronaught

Respuesta

4

Esto funciona de la suposición de que los eventos de Win32 son caros. No lo son, hay poco que puedo pensar que sea más barato que un evento. Una pista importante de que esto es así es que los diseñadores de .NET deciden que sería una buena idea usar un evento Win32 para implementar MRE y ARE.

El verdadero costo de su reemplazo es el mayor FUD que experimentará cuando tenga una carrera de enhebrado y no sepa cuál es la causa.

+1

Bueno, la suposición es que ARE y MRE usan un evento kernel bajo el capó que sería más costoso que un Monitor. Me encantaría que se demostrara que estaba equivocado :) – sweetlilmre

+0

Este artículo de microsoft detalla cuáles son rápidos: http://msdn.microsoft.com/en-us/library/ms228964.aspx –

+0

Desde la perspectiva de una transición kernel-mode en .NET, los eventos Win32 son caros, de ahí las ligeras adiciones en .NET 4 según la respuesta @Aaronaught anterior. – sweetlilmre

1

Desafortunadamente, la implementación correcta del monitor es bastante pesada dadas las primitivas de sincronización de Win32. Mi sospecha inicial es que "bloqueo" sería más pesado en el uso de recursos que un evento (y probablemente se construya sobre un evento).

+0

Estoy bastante seguro de que el bloqueo no se implementa de ninguna manera por un evento bajo el capó. IIRC hay estructuras de rosca especiales utilizadas específicamente para manejar el bloqueo de roscas y para hacer esto más ligero que los eventos. – sweetlilmre

+0

@sweetlilmre - Miré en la implementación de Rotor/SharedCCI, y utilizan algunos bloqueos de tipo spinlock, pero se reduce a un tipo de evento proporcionado por el host si es necesario. –

+0

Sí, giran ellos mismos, utilizando sus propios bits de sincronización. Lo único que no he asimilado es cómo lo hacen saber al programador de Windows. ¿Tienes un código loc? –

1

Una forma de optimizar el rendimiento de AutoResetEvent es mantener su estado (señalizado/no señalizado) en su propia variable, por lo que antes de realizar el viaje al kernel y utilizar el objeto de evento, simplemente puede verificar el estado de su aplicativo variable y permanecer en modo usuario todo el tiempo.
He publicado un demonstration de este concepto hace un par de meses.

Cuestiones relacionadas