it-swarm-eu.dev

Minuterie toutes les 5 minutes

Comment puis-je exécuter une fonction toutes les 5 minutes? Exemple: je veux exécuter sendRequest() uniquement aux heures 14:00, 14:05, 14:10, etc.

Je voudrais le faire par programme, en C #. L'application est un service Windows.

16
Simon

La réponse postée il y a six ans est utile. Cependant, à mon humble avis avec le C # moderne, il est maintenant préférable d’utiliser l’API Task- avec async et await. De plus, je diffère un peu des spécificités de la mise en œuvre, telles que la façon de gérer le calcul du retard et d’arrondir l’heure actuelle au prochain intervalle de cinq minutes.

Tout d'abord, supposons que la méthode sendRequest() renvoie void et ne comporte aucun paramètre. Supposons ensuite que l’exigence de base est de le lancer environ toutes les cinq minutes (c’est-à-dire qu’il n’est pas très important qu’il se déroule exactement à des intervalles de cinq minutes). Ensuite, cela peut être implémenté très facilement, comme ceci:

async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
{
    while (true)
    {
        action();
        await Task.Delay(interval, token);
    }
}

On peut appeler ça comme ça:

CancellationTokenSource tokenSource = new CancellationTokenSource();

Task timerTask = RunPeriodically(sendRequest, TimeSpan.FromMinutes(5), tokenSource.Token);

Lorsque tokenSource.Cancel() est appelé, la boucle sera interrompue par la variable TaskCanceledException envoyée à l'instruction await Task.Delay(...). Sinon, la méthode sendRequest() sera appelée toutes les cinq minutes (elle sera appelée immédiatement dès que la méthode RunPeriodically() sera appelée… vous pouvez réorganiser les instructions de la boucle si vous souhaitez également attendre la première fois).

C'est la version la plus simple. Si, au lieu de cela, vous souhaitez exécuter l'action exactement à des intervalles de cinq minutes, vous pouvez effectuer une opération similaire, mais calculer la durée et le délai d'exécution suivants pendant une durée appropriée. Par exemple:

// private field somewhere appropriate; it would probably be best to put
// this logic into a reusable class.
DateTime _nextRunTime;

async Task RunPeriodically(Action action,
    DateTime startTime, TimeSpan interval, CancellationToken token)
{
    _nextRunTime = startTime;

    while (true)
    {
        TimeSpan delay = _nextRunTime - DateTime.UtcNow;

        if (delay > TimeSpan.Zero)
        {                
            await Task.Delay(delay, token);
        }

        action();
        _nextRunTime += interval;
    }
}

Appelé comme ça:

CancellationTokenSource tokenSource = new CancellationTokenSource();
DateTime startTime = RoundCurrentToNextFiveMinutes();

Task timerTask = RunPeriodically(sendRequest,
    startTime, TimeSpan.FromMinutes(5), tokenSource.Token);

Où la méthode d'assistance RoundCurrentToNextFiveMinutes() ressemble à ceci:

DateTime RoundCurrentToNextFiveMinutes()
{
    DateTime now = DateTime.UtcNow,
        result = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);

    return result.AddMinutes(((now.Minute / 5) + 1) * 5);
}

Dans l'un ou l'autre exemple, l'objet timerTask peut être utilisé pour surveiller l'état de la boucle. Dans la plupart des cas, ce n'est probablement pas nécessaire. Mais si vous voulez pouvoir, par exemple await jusqu'à ce qu'une autre partie du code annule la boucle, cet objet est ce que vous utiliseriez.

Notez que la méthode Task.Delay() utilise un minuteur dans son implémentation. Ce qui précède n’est pas suggéré dans le but d’éviter l’utilisation d’une minuterie, mais plutôt de montrer une implémentation de l’objectif initial en utilisant les fonctionnalités modernes C # async/await. Cette version s'intégrera plus proprement avec un autre code qui utilise déjà async/await.

11
Peter Duniho

Utilisez System.Threading.Timer . Vous pouvez spécifier une méthode pour appeler périodiquement.

Exemple:

Timer timer = new Timer(Callback, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));

public void Callback(object state) {
    Console.WriteLine("The current time is {0}", DateTime.Now);
}

Vous pouvez utiliser le second paramètre pour transmettre l'état au rappel.

Notez que vous devez maintenir votre application en vie (par exemple, exécutez-la en tant que service).

En ce qui concerne la façon de s’assurer qu’il fonctionne à hh:mmmm % 5 == 0, vous pouvez procéder comme suit.

DateTime now = DateTime.Now;
int additionalMinutes = 5 - now.Minute % 5;
if(additionalMinutes == 0) {
    additionalMinutes = 5;
}
var nearestOnFiveMinutes = new DateTime(
    now.Year,
    now.Month,
    now.Day,
    now.Hour,
    now.Minute,
    0
).AddMinutes(additionalMinutes);
TimeSpan timeToStart = nearestOnFiveMinutes.Subtract(now);
TimeSpan tolerance = TimeSpan.FromSeconds(1);
if (timeToStart < tolerance) {
    timeToStart = TimeSpan.Zero;
}

var Timer = new Timer(callback, null, timeToStart, TimeSpan.FromMinutes(5));

Notez que tolerance est nécessaire au cas où ce code serait exécuté lorsque now est très proche du hh:mm avec mm % 5 == 0 le plus proche. Vous pouvez probablement vous en tirer avec une valeur inférieure à une seconde, mais je vous laisse cela.

19
jason

Utilisez System.Threading.Timer:

var timer = new Timer(TimerTick, null, TimeSpan.Zero, new TimeSpan(0, 0, 0, 1));

int lastMinute = 1;

void TimerTick(object state)
{
    var minute = DateTime.Now.Minutes;
    if (minute != lastMinute && minute % 5 == 0)
    {
        lastMinute = minute;
        //do stuff
    }
}

Cela peut paraître quelque peu maladroit et inefficace, car il doit appeler toutes les secondes, mais le nombre réel de cycles du processeur utilisés pour effectuer le contrôle une fois par seconde est totalement négligeable sur la plupart des matériels.

(Désolé si le code n'est pas correct à 100%; je n'ai pas de IDE sur moi pour le moment.

9
Rei Miyasaka

Vous pouvez créer un fil incluant une boucle comme celle-ci

void Runner(){
   while(1){
         Thread t = new thread( target_method );
         t.start();
         sleep(5 * 60 * 1000);
   }
}

C'est un peu rapide et sale mais ça va faire le travail :)

5
ykatchou

Vous feriez mieux de donner à votre propre thread qui exécute la méthode une condition

While(programStatus == ProgramStatus.Working)
{
   While (true)
   {
      // Do your stuff
   }
   Thread.Sleep(TheTimeYouWantTheThreadToWaitTillTheNextOperation);
}
3
Ilia

vous pourriez avoir un fil qui exécute cette première 

  1. vérifie combien de temps dormir jusqu'à la 5ème minute suivante (donc s'il est 13:59:51, il dort pendant 9 secondes) 
  2. fait l'action;
  3. puis dort pendant 5 minutes 
  4. passe à l'étape 2
3
Axarydax

Cette classe est tout ce dont vous avez besoin, vous définissez simplement le temps entre l'appel du délégué et votre délégué, et c'est tout :) 

http://msdn.Microsoft.com/en-us/library/system.threading.timer.aspx

3
Machinarius

Il existe plusieurs façons de procéder. Vous pouvez créer un chronomètre toutes les 5 minutes et le démarrer lorsque le temps atteint l’une des intervalles de 5 minutes, ou le faire fonctionner toutes les quelques secondes et vérifier si le temps est divisible par 5.

System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); // create a new timer
timer.interval = 300000; //300000 = 5 minutes

puis créez une fonction tick et ajoutez un gestionnaire d'événements

timer.Tick += new EventHandler(TimerTickHandler); //add the event handler
timer.Start(); //start the timer
2
Qwerty01

Ce que vous recherchez, c'est un code qui implémente des tâches "cron". Quelques bibliothèques le font déjà (quartz.net en fait partie). J'ai trouvé que beaucoup d'entre eux sont volumineux et ont de nombreuses fonctionnalités. Voici une implémentation allégée que j'ai utilisée dans certains projets:

http://blog.bobcravens.com/2009/10/an-event-based-cron-scheduled-job-in-c/

P.S. Mon serveur semble être en panne en ce moment. Je travaille sur ça.

J'espère que cela pourra aider.

Bob

2
rcravens

Je suis d'accord avec Peter Duniho - le C # moderne est bien meilleur dans ce domaine.

Je suggérerais d'utiliser le cadre réactif de Microsoft (NuGet "System.Reactive") et vous pouvez ensuite procéder ainsi:

IObservable<long> timer =
    Observable
        .Defer(() =>
        {
            var now = DateTimeOffset.Now;
            var result = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Offset);
            result = result.AddMinutes(((now.Minute / 5) + 1) * 5);
            return Observable.Timer(result, TimeSpan.FromMinutes(5.0));
        });

    timer.Subscribe(x => 
    {
        /* Your code here that run on the 5 minute every 5 minutes. */
    });

Je reçois ce genre de résultat:

 2016/08/11 14:40:00 +09: 30 
 2016/08/11 14:45:00 +09: 30 
 2016/08/11 14:50:00 +09: 30 
1
Enigmativity
0
jchase520