HOWTO · Csharp

Cómo hacer un temporizador de retraso en C#

Este artículo introduce cómo hacer un temporizador de retraso en C#. Incluye cuatro métodos diferentes: Thread.Sleep(), Task.Delay(), System.Timers.Timer y System.Threading.Timer.

En esta página

Para añadir un retraso en C#, elige Thread.Sleep() para bloquear el subproceso actual, Task.Delay() para esperar de forma asíncrona o una clase de temporizador para programar una devolución de llamada. Los ejemplos siguientes utilizan un intervalo de dos segundos y muestran cómo se comporta cada opción. No se han ejecutado en el entorno de ejecución proporcionado, por lo que no se afirma ninguna salida observada.

Usar el método Thread.Sleep() para crear un retraso en C#

Thread.Sleep() pausa el subproceso actual durante el número especificado de milisegundos. Como ese subproceso no puede realizar otro trabajo durante la espera, utiliza este enfoque solo cuando el bloqueo sea apropiado, como en un programa de consola sincrónico corto.

Este ejemplo completo imprime un mensaje, espera dos segundos y luego imprime otro mensaje:

using System;
using System.Threading;

class DelayExample
{
    static void Main()
    {
        Console.WriteLine("The delay starts.");
        Thread.Sleep(2000);
        Console.WriteLine("The delay ends.");
    }
}

El argumento está en milisegundos, por lo que 2000 solicita una espera de dos segundos. El tiempo real transcurrido puede variar según la planificación del sistema.

Usar el método Task.Delay() para crear un retraso en C#

Task.Delay() crea una tarea que se completa después del intervalo solicitado. Esperarla con await en un método asíncrono permite que ese método ceda mientras espera, a diferencia de Thread.Sleep(), que ocupa el subproceso actual. Llama al método asíncrono desde el flujo asíncrono existente de tu aplicación.

Este ejemplo completo mantiene la espera asíncrona en un método separado y utiliza un punto de entrada sincrónico para llamarlo:

using System;
using System.Threading.Tasks;

class DelayExample
{
    static void Main()
    {
        RunAsync().GetAwaiter().GetResult();
    }

    static async Task RunAsync()
    {
        Console.WriteLine("Before the delay");
        await Task.Delay(2000);
        Console.WriteLine("After the delay");
    }
}

La expresión await reanuda RunAsync después de que se completa la tarea de retraso. En una aplicación que ya utiliza métodos asíncronos, espera Task.Delay(2000) directamente con await en lugar de bloquear el punto de entrada para esperar la tarea.

Usar la clase System.Timers.Timer para crear un retraso en C#

System.Timers.Timer genera un evento Elapsed después de su Interval en milisegundos. Establece AutoReset en false para un único evento; establécelo en true cuando el evento deba repetirse en el intervalo. La devolución de llamada está controlada por eventos, por lo que iniciar un temporizador no pausa la siguiente línea de código.

Este ejemplo completo espera el evento único antes de que el programa de consola finalice:

using System;
using System.Threading;
using System.Timers;

class DelayExample
{
    static void Main()
    {
        using (var completed = new ManualResetEvent(false))
        using (var timer = new System.Timers.Timer(2000))
        {
            timer.AutoReset = false;
            timer.Elapsed += (sender, args) =>
            {
                Console.WriteLine("Timer elapsed.");
                completed.Set();
            };

            Console.WriteLine("Timer started.");
            timer.Start();
            completed.WaitOne();
        }
    }
}

WaitOne() mantiene vivo este ejemplo de consola hasta que el controlador del evento señala la finalización. En una aplicación de ejecución más prolongada, mantén la aplicación activa según su bucle de eventos normal o la duración de su servicio, en lugar de añadir una espera bloqueante únicamente para el temporizador.

Usar la clase System.Threading.Timer para crear un retraso en C#

System.Threading.Timer programa una devolución de llamada con un tiempo de vencimiento inicial y un período. Ambos valores están en milisegundos. Usa Timeout.Infinite para el período cuando la devolución de llamada deba ejecutarse una vez; proporciona un período finito para programar devoluciones de llamada repetidas. La devolución de llamada se ejecuta por separado del código que inicia el temporizador.

Este ejemplo completo programa una devolución de llamada después de dos segundos y la espera antes de finalizar:

using System;
using System.Threading;

class DelayExample
{
    static void Main()
    {
        using (var completed = new ManualResetEvent(false))
        using (var timer = new System.Threading.Timer(
            state =>
            {
                Console.WriteLine("Timer callback.");
                completed.Set();
            },
            null,
            2000,
            Timeout.Infinite))
        {
            Console.WriteLine("Timer scheduled.");
            completed.WaitOne();
        }
    }
}

El tiempo de vencimiento programa la primera devolución de llamada dos segundos después de la construcción. Timeout.Infinite impide que se programe otra devolución de llamada. La espera mantiene vivo este ejemplo de consola el tiempo suficiente para recibir la devolución de llamada; una aplicación con su propio bucle de eventos puede utilizar esa duración en su lugar.

Conclusión

Usa Thread.Sleep() para una pausa sincrónica sencilla cuando bloquear el subproceso actual sea aceptable. Prefiere Task.Delay() con await en el código asíncrono para que el método pueda ceder mientras espera. Elige System.Timers.Timer o System.Threading.Timer cuando necesites una programación basada en devoluciones de llamada, con AutoReset o el período del temporizador controlando la repetición. Las devoluciones de llamada de los temporizadores programan trabajo; no pausan la ejecución secuencial y su temporización no es una garantía exacta de reloj de pared.