Pregunta

Tengo un método que debería posponerse funcionando durante un período de tiempo especificado.

¿Debo usar

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

o

Timer timer = new Timer(o => action(), null, millisecond, -1);

He leído algunas artículos sobre el uso de Thread.Sleep es un mal diseño. Pero yo no entiendo muy bien por qué.

Sin embargo, para el uso del temporizador, temporizador tiene disponer método. Dado que la ejecución se retrasa, no sé cómo deshacerse del temporizador. ¿Tiene alguna sugerencia?

O si usted tiene códigos alternativos para la ejecución retardada se aprecian también.

¿Fue útil?

Solución

Una diferencia es que System.Threading.Timer despacha la devolución de llamada en un subproceso del grupo de subprocesos, en lugar de crear un nuevo hilo cada vez. Si necesita que esto ocurra más de una vez durante la vida de su aplicación, esto ahorrará la sobrecarga de crear y destruir un haz de hilos (un proceso que es muy intensivo en recursos, y cuando el artículo se hace referencia señala), ya que se simplemente volver a utilizar subprocesos en el grupo, y si va a tener más de un temporizador funcionando al mismo tiempo que significa que tendrá un menor número de subprocesos que se ejecutan a la vez (también el ahorro considerable de recursos).

En otras palabras, Timer va a ser mucho más eficiente. También puede ser más preciso, ya que Thread.Sleep sólo se garantiza que esperar por lo menos tan larga como la cantidad de tiempo que especifique (el sistema operativo puede ponerlo en reposo durante mucho más tiempo). Por supuesto, Timer todavía no va a ser exactamente precisa, pero la intención es para disparar la devolución de llamada lo más cerca posible del tiempo especificado como sea posible, mientras que esto no es necesariamente la intención de Thread.Sleep.

En cuanto a la destrucción de la Timer, la devolución de llamada puede aceptar un parámetro, por lo que puede ser capaz de pasar el Timer como el parámetro y llamar a Dispose en la devolución de llamada (aunque no he probado esto - supongo que es posible que el temporizador puede ser bloqueado durante la devolución de llamada).

Editar:. No, supongo que no se puede hacer esto, ya que hay que especificar el parámetro de devolución de llamada en el propio constructor Timer

Tal vez algo como esto? (De nuevo, en realidad no han probado)

class TimerState
{
    public Timer Timer;
}

... y para iniciar el temporizador:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

El bloqueo debe impedir la devolución de llamada del temporizador de tratar de liberar el temporizador antes de que se haya ajustado el campo Timer.


Adición: Como el comentarista señaló, si action() hace algo con la interfaz de usuario, y luego usando un System.Windows.Forms.Timer es probablemente una mejor opción, ya que se ejecutará la devolución de llamada en el subproceso de interfaz de usuario. Sin embargo, si este no es el caso, y es hasta Thread.Sleep vs Threading.Timer, Threading.Timer es el camino a seguir.

Otros consejos

ThreadPool.RegisterWaitForSingleObject en lugar de temporizador:

//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);

Creo Thread.Sleep está bien si usted realmente quiere hacer una pausa en la aplicación de una determinada cantidad de tiempo. Creo que la razón la gente dice que es un mal diseño es debido a que en la mayoría de las situaciones la gente no desea realmente la aplicación para hacer una pausa.

Por ejemplo, yo estaba trabajando en un cliente POP3 donde el programador estaba usando Thread.Sleep (1000) que esperar mientras la toma recuperada electrónico. En esa situación era mejor para conectar un controlador de eventos para el zócalo y continuar la ejecución del programa después de la toma de corriente se había completado.

Me acuerdo de implementar una solución similar a uno de Eric. Este es sin embargo un funcionamiento uno;)

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; }
    }

La única carne que tengo con el System.Timer es que la mayoría de las veces que he visto que se usa para los largos retrasos (horas, minutos) en los servicios de votación y los desarrolladores a menudo se olvide de poner en marcha el evento antes comienzan el temporizador. Esto significa que si comienzo a la aplicación o servicio, tengo que esperar hasta que el contador de tiempo transcurrido (horas, minutos) antes de que realmente se ejecuta.

Por supuesto, esto no es un problema con el contador de tiempo, pero creo que su a menudo una utilización inadecuada por su causa demasiado fácil uso indebido.

No se @miniscalope no utilice ThreadPool.RegisterWaitForSingleObject en lugar de temporizador, System.Threading.Timer pondrá en cola una devolución de llamada a ser ejecutado en un subproceso del grupo de subprocesos cuando haya transcurrido el tiempo y no requiere un mango de espera, esperar único objeto será atar un hilo de subprocesos en espera para el evento debe señalizarse o el tiempo de espera para expirar antes de que el subproceso llama a la devolución de llamada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top