2011-08-14 15 views
5

Tengo una función que deseo invocar cada x segundos, pero quiero que sea segura para subprocesos.Temporizadores no reentrantes

¿Puedo configurar este comportamiento cuando estoy creando el temporizador? (No me importa qué temporizador .NET uso, solo quiero que sea seguro para subprocesos).

Sé que puedo implementar bloqueos dentro de mi función de devolución de llamada, pero creo que sería más elegante si estuviera en el nivel del temporizador.

Mi función de devolución de llamada y el entorno no están relacionados con una IU.

[Editar 1] Simplemente no quiero que haya más de un hilo dentro de mi función de devolución de llamada.

[Editar 2] Quiero mantener el bloqueo dentro del nivel de temporizador, debido a que el temporizador es responsable de cuándo llamar a mi devolución de llamada, y aquí hay una situación particular, cuando no quiero llamar a mi función de devolución de llamada. Así que creo que cuándo llamar es responsabilidad del temporizador.

+2

que no dan suficiente información ... hace nada a su acceso a las funciones de cualquier otra parte del acceso al programa? Si no es así, entonces ya es seguro (con y sin temporizador) ... En caso afirmativo, entonces el temporizador no tiene nada con la seguridad del hilo, pero debe usar un candado, etc. – Yahia

+1

** por qué ** desea mantener el bloqueo de la función de devolución de llamada exactamente? –

+0

@Paul - ver mi edición 2 – Delashmate

Respuesta

16

Supongo que, como su pregunta no está del todo clara, quiere asegurarse de que su temporizador no pueda volver a ingresar su devolución de llamada mientras está procesando una devolución de llamada, y quiere hacerlo sin bloquearlo. Puede lograr esto usando System.Timers.Timer y asegurándose de que la propiedad AutoReset esté configurada como falsa.Esto asegurará que usted tiene que activar el temporizador en cada intervalo manualmente, evitando así cualquier reentrada:

public class NoLockTimer : IDisposable 
{ 
    private readonly Timer _timer; 

    public NoLockTimer() 
    { 
     _timer = new Timer { AutoReset = false, Interval = 1000 }; 

     _timer.Elapsed += delegate 
     { 
      //Do some stuff 

      _timer.Start(); // <- Manual restart. 
     }; 

     _timer.Start(); 
    } 

    public void Dispose() 
    { 
     if (_timer != null) 
     { 
      _timer.Dispose(); 
     } 
    } 
} 
+0

entiendes completamente mi pregunta, y te gustó tu solución, esperaba que este comportamiento se insertara dentro de una clase de temporizadores, pero esto es suficiente ... – Delashmate

+0

Esto es precisamente lo que hice y funciona muy bien. FYI: este ejemplo sería un poco más claro si no usara un delegado anónimo. –

2

Sé que puedo aplicar bloqueos dentro de mi función de devolución de llamada, pero yo creo que será más elegante si va a estar en el nivel de temporizador

Si es necesario bloqueo entonces, ¿cómo podría un temporizador disponer que ? Está buscando un regalo de promoción mágico.

Re Edit1:

sus opciones son System.Timers.Timer y System.Threading.Timer, ambos necesitan precauciones contra la re-entrada. Consulte this page y busque Tratar con la reentrada de evento de temporizador sección.

+0

Vea mi Editar 1, ¿tiene sentido para usted? – Delashmate

+0

@Hank Leí la sección que mencionas, me gustó tu respuesta, pero la chibacity me dio la respuesta más adecuada a mi pregunta, porque quiero administrarla dentro de la configuración del temporizador ... y no ingresar la lógica dentro de la función de devolución de llamada, gracias de nuevo – Delashmate

-1

¿Cómo el temporizador podría conocer sus datos compartidos?

La devolución de llamada del temporizador se ejecuta en algún subproceso de ThreadPool. Por lo tanto, tendrá al menos 2 hilos:

  1. Su hilo principal donde se crea y se inicia el temporizador;
  2. Subproceso de ThreadPool para iniciar la devolución de llamada.

Y es su responsabilidad proporcionar el trabajo correcto con sus datos compartidos.

Re edits: chibacity proporciona el ejemplo perfecto.

+0

@downvoter: ¿Qué pasa con mi respuesta? –

3

Como complemento de solución de Tim Lloyd para System.Timers.Timer, he aquí una solución para evitar la reentrada para los casos en los que desee utilizar System.Threading.Timer lugar.

TimeSpan DISABLED_TIME_SPAN = TimeSpan.FromMilliseconds(-1); 

TimeSpan interval = TimeSpan.FromSeconds(1); 
Timer timer = null; // assign null so we can access it inside the lambda 

timer = new Timer(callback: state => 
{ 
    doSomeWork(); 
    try 
    { 
    timer.Change(interval, DISABLED_TIME_SPAN); 
    } 
    catch (ObjectDisposedException timerHasBeenDisposed) 
    { 
    } 
}, state: null, dueTime: interval, period: DISABLED_TIME_SPAN); 

Creo que no quiere interval a acceder en el interior de la devolución de llamada, pero eso es ser fácil de solucionar, si se quiere: poner lo anterior en una clase NonReentrantTimer que envuelve la clase del BCL Timer. Luego, pasaría la devolución de llamada doSomeWork como parámetro. Un ejemplo de dicha clase:

public class NonReentrantTimer : IDisposable 
{ 
    private readonly TimerCallback _callback; 
    private readonly TimeSpan _period; 
    private readonly Timer _timer; 

    public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) 
    { 
     _callback = callback; 
     _period = period; 
     _timer = new Timer(Callback, state, dueTime, DISABLED_TIME_SPAN); 
    } 

    private void Callback(object state) 
    { 
     _callback(state); 
     try 
     { 
      _timer.Change(_period, DISABLED_TIME_SPAN); 
     } 
     catch (ObjectDisposedException timerHasBeenDisposed) 
     { 
     } 
    } 


    public void Dispose() 
    { 
     _timer.Dispose(); 
    } 
} 
0
/// <summary> 
/// The C#6 version 
/// </summary> 
public class TimerNicer : IDisposable { 
    private Action OnElapsed { get; } 

    [NotNull] 
    private System.Timers.Timer Timer { get; } = new System.Timers.Timer { AutoReset = false, Interval = 1 }; 

    public TimerNicer(Action onElapsed) { 

     this.OnElapsed = onElapsed ?? (() => { 
     }); 

     this.Timer.Elapsed += (sender, args) => { 

      this.Timer.Stop(); // Why not stop the timer here with this? 

      try { 
       this.OnElapsed(); // do stuff here 
      } 
      catch (Exception exception) { 
       Console.WriteLine(exception); 
      } 
      finally { 
       this.Timer.Start(); 
      } 
     }; 

     this.Timer.Start(); 
    } 

    public void Dispose() => this.Timer.Dispose(); 
} 
Cuestiones relacionadas