c# - pause - Compare usando Thread.Sleep y Timer para la ejecución retrasada
timer sleep c# (6)
Tengo un método que debería retrasarse por un período de tiempo específico.
Debería usar
Thread thread = new Thread(() => {
Thread.Sleep(millisecond);
action();
});
thread.IsBackground = true;
thread.Start();
O
Timer timer = new Timer(o => action(), null, millisecond, -1);
Había leído algunos articles sobre el uso de Thread.Sleep
. Thread.Sleep
es un mal diseño. Pero realmente no entiendo por qué.
Pero para usar el temporizador, el temporizador tiene un método de eliminación. Dado que la ejecución se retrasa, no sé cómo deshacerme del temporizador. ¿Tienes alguna sugerencia?
O si tiene códigos alternativos para la ejecución retrasada también se aprecian.
@miniscalope No use ThreadPool.RegisterWaitForSingleObject en lugar de timer, System.Threading.Timer pondrá en cola una devolución de llamada que se ejecutará en un hilo del grupo de subprocesos cuando haya transcurrido el tiempo y no requiera un identificador de espera, esperar a que un solo objeto atar un subproceso de subprocesamiento esperando a que se señalice el evento o expire el tiempo de espera antes de que el subproceso llame a la devolución de llamada.
Creo que Thread.Sleep está bien si realmente quieres pausar la aplicación por un período de tiempo específico. Creo que la razón por la que las personas dicen que es un mal diseño es porque en la mayoría de las situaciones las personas no quieren que la aplicación haga una pausa.
Por ejemplo, estaba trabajando en un cliente pop3 donde el programador estaba usando Thread.Sleep (1000) para esperar mientras el socket recuperaba el correo. En esa situación, era mejor conectar un controlador de eventos al zócalo y continuar la ejecución del programa después de que el socket se hubiera completado.
El único problema que tengo con System.Timer es que la mayoría de las veces lo he visto utilizado para largas demoras (horas, minutos) en servicios de votación y los desarrolladores a menudo se olvidan de lanzar el evento antes de que inicien el temporizador. Esto significa que si inicio la aplicación o el servicio, tengo que esperar hasta que el temporizador haya transcurrido (horas, minutos) antes de que se ejecute.
Claro, esto no es un problema con el temporizador, pero creo que a menudo se usa incorrectamente porque es muy fácil de usar.
Recuerdo implementar una solución similar a la de Eric. Sin embargo, este es un trabajo;)
class OneTimer
{
// Created by Roy Feintuch 2009
// Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
{
var td = new TimerDisposer();
var timer = new Timer(myTdToKill =>
{
try
{
cb();
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
}
finally
{
((TimerDisposer)myTdToKill).InternalTimer.Dispose();
}
},
td, dueTime, TimeSpan.FromMilliseconds(-1));
td.InternalTimer = timer;
}
}
class TimerDisposer
{
public Timer InternalTimer { get; set; }
}
Una diferencia es que System.Threading.Timer distribuye la devolución de llamada en un subproceso de grupo de subprocesos, en lugar de crear un nuevo subproceso cada vez. Si necesita que esto suceda más de una vez durante la vida de su aplicación, esto ahorrará la sobrecarga de crear y destruir un grupo de hilos (un proceso que consume muchos recursos, como señala el artículo que referencia), ya que lo hará solo reutilice los hilos en el grupo, y si va a tener más de un temporizador funcionando a la vez, significa que tendrá menos hilos ejecutándose al mismo tiempo (lo que también ahorrará considerables recursos).
En otras palabras, Timer va a ser mucho más eficiente. También puede ser más preciso, ya que Thread.Sleep solo garantiza que esperará al menos el tiempo que especifique (el sistema operativo puede ponerlo a dormir durante mucho más tiempo). De acuerdo, Timer aún no será exactamente exacto, pero la intención es disparar la devolución de llamada lo más cerca posible del tiempo especificado, mientras que NO es necesariamente la intención de Thread.Sleep.
En cuanto a la destrucción del temporizador, la devolución de llamada puede aceptar un parámetro, por lo que puede pasar el temporizador como parámetro y llamar a Dispose en la devolución de llamada (aunque no lo he probado, supongo que es posible que el temporizador podría estar bloqueado durante la devolución de llamada).
Editar: No, supongo que no puedes hacer esto, ya que tienes que especificar el parámetro de devolución de llamada en el propio constructor del temporizador.
Tal vez algo como esto? (De nuevo, no lo he probado)
class TimerState
{
public Timer Timer;
}
... y para iniciar el cronómetro:
TimerState state = new TimerState();
lock (state)
{
state.Timer = new Timer((callbackState) => {
action();
lock (callbackState) { callbackState.Timer.Dispose(); }
}, state, millisecond, -1);
}
El bloqueo debe evitar que la devolución de llamada del temporizador intente liberar el temporizador antes de que se haya establecido el campo del temporizador.
Adición: como señaló el comentarista, si action () hace algo con la interfaz de usuario, entonces usar System.Windows.Forms.Timer es probablemente una mejor opción, ya que ejecutará la devolución de llamada en el subproceso de la interfaz de usuario. Sin embargo, si este no es el caso, y depende de Thread.Sleep vs. Threading.Timer, Threading.Timer es el camino a seguir.
use ThreadPool.RegisterWaitForSingleObject
lugar de timer:
//Wait 5 seconds then print out to console.
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);