¿Cómo puedo generar un evento cada hora (o intervalo de tiempo específico cada hora) en .NET?

StackOverflow https://stackoverflow.com/questions/307798

  •  08-07-2019
  •  | 
  •  

Pregunta

Estoy trabajando en un pequeño rastreador web que se ejecutará en la bandeja del sistema y rastreará un sitio web cada hora en punto.

¿Cuál es la mejor manera de hacer que .NET genere un evento cada hora o algún otro intervalo para realizar alguna tarea? Por ejemplo, quiero ejecutar un evento cada 20 minutos según el tiempo. El evento se planteará en:

00:20
00:40
01:00
01:20
01:40

y así sucesivamente. La mejor manera en que puedo pensar para hacer esto es creando un bucle en un hilo, que comprueba constantemente si el tiempo es divisible por un intervalo determinado y genera un evento de devolución de llamada si se alcanza el tiempo. Siento que tiene que haber una mejor manera.

Usaría un Timer pero preferiría algo que siga un " horario " que se ejecuta en la hora o algo por el estilo.

Sin configurar mi aplicación en el programador de tareas de Windows, ¿es esto posible?

ACTUALIZACIÓN:
Estoy agregando mi algoritmo para calcular el intervalo de tiempo para un temporizador. Este método lleva un " minuto " parámetro, que es a qué hora el temporizador debe activar un tic. Por ejemplo, si el " minuto " el parámetro es 20, entonces el temporizador marcará los intervalos en el cronograma anterior.

int CalculateTimerInterval(int minute)
{
    if (minute <= 0)
        minute = 60;
    DateTime now = DateTime.Now;

    DateTime future = now.AddMinutes((minute - (now.Minute % minute))).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);

    TimeSpan interval = future - now;

    return (int)interval.TotalMilliseconds;
}

Este código se usa de la siguiente manera:

static System.Windows.Forms.Timer t;
const int CHECK_INTERVAL = 20;


static void Main()
{
    t = new System.Windows.Forms.Timer();
    t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
    t.Tick += new EventHandler(t_Tick);
    t.Start();
}

static void t_Tick(object sender, EventArgs e)
{
    t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
}
¿Fue útil?

Solución

System.Timers.Timer . Si desea correr en momentos específicos del día, necesitará calcular cuánto tiempo falta para la próxima vez y configurarlo como su intervalo.

Esta es solo la idea básica. Dependiendo de cuán preciso sea necesario, puede hacer más.

int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;  

Otros consejos

Puede encontrar ayuda de Quartz.net http://quartznet.sourceforge.net/

Aquí hay un ejemplo de un sistema liviano que usa sincronización de subprocesos y una llamada asíncrona.

Sé que hay algunas desventajas, pero me gusta usar esto en lugar de un temporizador al iniciar un proceso de larga ejecución (como los servicios programados de backend). Como se ejecuta en línea en el subproceso del temporizador, no tiene que preocuparse de que se vuelva a iniciar antes de que finalice la llamada original. Esto podría extenderse bastante para hacer que use una variedad de fechas y horas como los tiempos de activación o para agregarle más habilidades. Estoy seguro de que algunos de ustedes conocen algunas formas mejores.

    public Form1()
    {
        InitializeComponent();

        //some fake data, obviously you would have your own.
        DateTime someStart = DateTime.Now.AddMinutes(1);
        TimeSpan someInterval = TimeSpan.FromMinutes(2);

        //sample call
        StartTimer(someStart,someInterval,doSomething);
    }

    //just a fake function to call
    private bool doSomething()
    {
        DialogResult keepGoing = MessageBox.Show("Hey, I did something! Keep Going?","Something!",MessageBoxButtons.YesNo);
        return (keepGoing == DialogResult.Yes);
    }

    //The following is the actual guts.. and can be transplanted to an actual class.
    private delegate void voidFunc<P1,P2,P3>(P1 p1,P2 p2,P3 p3);
    public void StartTimer(DateTime startTime, TimeSpan interval, Func<bool> action)
    {
        voidFunc<DateTime,TimeSpan,Func<bool>> Timer = TimedThread;
        Timer.BeginInvoke(startTime,interval,action,null,null);
    }

    private void TimedThread(DateTime startTime, TimeSpan interval, Func<bool> action)
    {
        bool keepRunning = true;
        DateTime NextExecute = startTime;
        while(keepRunning)
        {
            if (DateTime.Now > NextExecute)
            {
                keepRunning = action.Invoke();
                NextExecute = NextExecute.Add(interval);
            }
            //could parameterize resolution.
            Thread.Sleep(1000);
        }            
    }

Otra estrategia para esto sería registrar la ÚLTIMA VEZ que se ejecutó el proceso y determinar si ha transcurrido el intervalo deseado desde ese momento. En esta estrategia, codificaría su evento para que se dispare si el tiempo transcurrido es igual O MAYOR AL intervalo deseado. De esta manera, puede manejar instancias en las que se podrían pasar largos intervalos (una vez al día, por ejemplo) si la computadora estuviera inactiva por alguna razón.

Entonces, por ejemplo:

  • lastRunDateTime = 5/2/2009 a las 8pm
  • Quiero ejecutar mi proceso cada 24 horas
  • En un evento de temporizador, verifique si han pasado 24 horas o MÁS desde la última vez que se ejecutó el proceso.
  • En caso afirmativo, ejecute el proceso, actualice lastRunDateTime agregando el intervalo deseado (24 horas en este caso, pero lo que sea que necesite)

Obviamente, para que esto se recupere después de que el sistema se haya caído, deberá almacenar lastRunDateTime en un archivo o base de datos en algún lugar para que el programa pueda continuar donde se quedó en la recuperación.

System.Windows.Forms.Timer (o System.Timers.Timer)

pero como ahora dice que no desea usar Timers, puede ejecutar un proceso de espera liviano en otro hilo (verifique el tiempo, duerma unos segundos, verifique nuevamente ...) o cree un componente que genere evento (utilizando un proceso de espera ligero) en ciertos horarios o intervalos programados

Mi objetivo es ejecutar una importación alrededor de las 03:00 todas las noches.

Aquí está mi enfoque, usando System.Timers.Timer:

private Timer _timer;
private Int32 _hours = 0;
private Int32 _runAt = 3;

protected override void OnStart(string[] args)
{
    _hours = (24 - (DateTime.Now.Hour + 1)) + _runAt;
    _timer = new Timer();
    _timer.Interval = _hours * 60 * 60 * 1000;
    _timer.Elapsed += new ElapsedEventHandler(Tick);
    _timer.Start();
}

void Tick(object sender, ElapsedEventArgs e)
{
    if (_hours != 24)
    {
        _hours = 24;
        _timer.Interval = _hours * 60 * 60 * 1000;
    }

    RunImport();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top