Como posso gerar um evento a cada hora (ou tempo específico intervalo de hora em hora) em .NET?

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

  •  08-07-2019
  •  | 
  •  

Pergunta

Eu estou trabalhando em um pouco de web crawler que será executado na bandeja do sistema e rastrear um web site de hora em hora.

Qual é a melhor maneira de obter .NET para elevar um evento a cada hora ou algum outro intervalo para executar alguma tarefa. Por exemplo, eu quero correr um evento a cada 20 minutos com base no tempo. O evento seria levantado em:

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

e assim por diante. A melhor maneira que eu posso pensar de fazer isso é através da criação de um laço em um segmento, que verifica constantemente se o tempo é divisível por um determinado intervalo e gera um evento de retorno de chamada, se o tempo for atingido. Eu sinto que há tem que haver uma maneira melhor.

eu usaria um Timer mas eu prefiro algo que segue um "cronograma" que é executado na hora ou algo nesse sentido.

Sem a criação de meu aplicativo no agendador de tarefas do Windows é isto possível?

UPDATE:
Eu estou adicionando meu algoritmo para calcular o intervalo de tempo para um temporizador. Este método tem um parâmetro "minute", que é o tempo que o temporizador deve desencadear um carrapato. Por exemplo, se o parâmetro "minute" é 20, então o temporizador vai assinalar nos intervalos do calendário acima.

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 é utilizado da seguinte forma:

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);
}
Foi útil?

Solução

System.Timers.Timer . Se você deseja executar em horários específicos do dia, você terá que descobrir quanto tempo é até a próxima vez e defina-o como o seu intervalo.

Esta é apenas a idéia básica. Dependendo de como precisa você precisa ser você pode fazer mais.

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

Outras dicas

Você pode encontrar ajuda de Quartz.net http://quartznet.sourceforge.net/

Aqui está um exemplo de um sistema leve utilizando sincronismo fio e uma chamada asynch.

Eu sei que há algumas desvantagens, mas eu gosto de usar isso em vez de um temporizador quando iniciando um processo de longa duração (como serviços de back-end schedualed). Desde que corre em linha no segmento de timer, você não precisa se preocupar com isso ficando expulso novamente antes que a chamada original foi concluída. Isso pode ser estendido um pouco para torná-lo usar uma matriz de datetimes como os tempos de disparo ou adicionar mais algumas habilidades a ele. Estou certo de que alguns de vocês lá fora, saber algumas maneiras melhores.

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

Outra estratégia para isso seria para gravar o último tempo que o processo foi executado e determinar se o intervalo desejado tenha decorrido desde aquela época. Nesta estratégia, você codificar seu evento para incêndio, se o tempo decorrido for igual ou maior do que o intervalo desejado. Desta forma, você pode lidar com casos em longos intervalos (uma vez por dia, por exemplo) poderiam ser perdidas se o computador fosse para baixo por algum motivo.

Assim, por exemplo:

  • lastRunDateTime = 5/2/2009 às 8h
  • Eu quero correr o meu processo a cada 24 horas
  • Em um evento timer, verifique se 24 horas ou mais passaram desde a última vez que o processo foi executado.
  • Se sim, executar o processo, atualização lastRunDateTime adicionando o intervalo desejado para ele (24 horas neste caso, mas o que você precisa que ele seja)

Obviamente, para este se recuperar após o sistema ter ido para baixo, você vai precisar para armazenar lastRunDateTime em algum lugar do arquivo ou banco de dados para que o programa poderia pegar de onde parou na recuperação.

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

mas desde agora você diz que não quer usar Timers, você pode executar um processo de espera leve em outro segmento (tempo de verificação, o sono de alguns segundos, o tempo de verificação de novo ...) ou fazer um componente que gera um evento (usando um processo de espera leve) em determinados momentos ou intervalos programados

Meu objetivo é executar uma importação em torno de 3:00 cada noite.

Aqui está a minha abordagem, 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top