Je souhaite qu'un événement soit déclenché dans mon application et qu'il s'exécute en continu pendant la journée à une certaine heure, disons à 16 h 00. J'ai pensé à faire fonctionner le chronomètre toutes les secondes et lorsque l'heure est égale à 16 h, lancez l'événement. Ça marche. Mais je me demande s'il existe un moyen de récupérer le rappel une fois à 16h00 et de ne pas avoir à continuer de vérifier.
Que diriez-vous de quelque chose comme ça, en utilisant le System.Threading.Timer
classe?
var t = new Timer(TimerCallback);
// Figure how much time until 4:00
DateTime now = DateTime.Now;
DateTime fourOClock = DateTime.Today.AddHours(16.0);
// If it's already past 4:00, wait until 4:00 tomorrow
if (now > fourOClock)
{
fourOClock = fourOClock.AddDays(1.0);
}
int msUntilFour = (int)((fourOClock - now).TotalMilliseconds);
// Set the timer to elapse only once, at 4:00.
t.Change(msUntilFour, Timeout.Infinite);
Notez que si vous utilisez un System.Threading.Timer
, le rappel spécifié par TimerCallback
sera exécuté sur un thread de pool de threads (non-UI) - donc si vous prévoyez de faire quelque chose avec votre UI à 4h00, vous devrez marshaler le code de manière appropriée (par exemple, en utilisant Control.Invoke
dans une application Windows Forms ou Dispatcher.Invoke
dans une application WPF).
À partir de .NET 4.5, il existe une solution vraiment propre:
public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
action();
}
Voici une solution sans async/wait:
public void Execute(Action action, DateTime ExecutionTime)
{
Task WaitTask = Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
WaitTask.ContinueWith(_ => action);
WaitTask.Start();
}
Il convient de noter que cela ne fonctionne que pendant environ 24 jours en raison de la valeur maximale int32, ce qui est suffisant pour votre cas, mais il convient de le noter.
Vous pouvez utiliser Planificateur de tâches sur Windows Voir exemple de déclencheur quotidien pour plus de détails.
ou utilisez le code ci-dessous si vous voulez l'écrire vous-même:
public void InitTimer()
{
DateTime time = DateTime.Now;
int second = time.Second;
int minute = time.Minute;
if (second != 0)
{
minute = minute > 0 ? minute-- : 59;
}
if (minute == 0 && second == 0)
{
// DoAction: in this function also set your timer interval to 24 hours
}
else
{
TimeSpan span = //new daily timespan, previous code was hourly: new TimeSpan(0, 60 - minute, 60 - second);
timer.Interval = (int) span.TotalMilliseconds - 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
void timer_Tick(object sender, EventArgs e)
{
timer.Interval = ...; // 24 hours
// DoAction
}
Prenant l'exemple de VoteCoffees, voici une solution compacte basée sur les événements:
public class DailyTrigger
{
readonly TimeSpan triggerHour;
public DailyTrigger(int hour, int minute = 0, int second = 0)
{
triggerHour = new TimeSpan(hour, minute, second);
InitiateAsync();
}
async void InitiateAsync()
{
while (true)
{
var triggerTime = DateTime.Today + triggerHour - DateTime.Now;
if (triggerTime < TimeSpan.Zero)
triggerTime = triggerTime.Add(new TimeSpan(24,0,0));
await Task.Delay(triggerTime);
OnTimeTriggered?.Invoke();
}
}
public event Action OnTimeTriggered;
}
Consommateur: `
void Main()
{
var trigger = new DailyTrigger(16); // every day at 4:00pm
trigger.OnTimeTriggered += () =>
{
// Whatever
};
Console.ReadKey();
}
.NET a beaucoup de classes de temporisation, mais elles prennent toutes des intervalles de temps par rapport à l'heure actuelle. Avec un temps relatif, il y a beaucoup de choses à considérer avant de démarrer votre minuterie et à surveiller pendant que votre minuterie fonctionne.
Le système d'exploitation est bien placé pour gérer cette complexité. Le code d'application s'exécutant sur .NET ne l'est pas.
Pour Windows, le package NuGet AbsoluteTimer encapsule un minuteur de système d'exploitation qui expire à une heure absolue.
Le planificateur de tâches est une meilleure option, et il peut être facilement utilisé en C #, http://taskscheduler.codeplex.com/
Echo to Dan's solution, using Timercallback est une solution rapide et soignée. Dans la méthode que vous souhaitez planifier l'exécution d'une tâche ou d'un sous-programme, utilisez ce qui suit:
t = New Timer(Sub()
'method call or code here'
End Sub, Nothing, 400, Timeout.Infinite)
l'utilisation de 'Timeout.Infinite' garantira que le rappel ne sera exécuté qu'une seule fois après 400 ms. J'utilise VB.Net.
Et cette solution?
Sub Main()
Dim t As New Thread(AddressOf myTask)
t.Start()
Console.ReadLine()
End Sub
Private Sub myTask()
Dim a = "14:35"
Dim format = "dd/MM/yyyy HH:mm:ss"
Dim targetTime = DateTime.Parse(a)
Dim currentTime = DateTime.Parse(Now.ToString(format))
Console.WriteLine(currentTime)
Console.WriteLine("target time " & targetTime)
Dim bb As TimeSpan = targetTime - currentTime
If bb.TotalMilliseconds < 0 Then
targetTime = targetTime.AddDays(1)
bb = targetTime - currentTime
End If
Console.WriteLine("Going to sleep at " & Now.ToString & " for " & bb.TotalMilliseconds)
Thread.Sleep(bb.TotalMilliseconds)
Console.WriteLine("Woke up at " & Now.ToString(format))
End Sub
J'ai fait cette façon de tirer 7 heures du matin chaque matin
bool _ran = false; //initial setting at start up
private void timer_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour == 7 && _ran==false)
{
_ran = true;
Do_Something();
}
if(DateTime.Now.Hour != 7 && _ran == true)
{
_ran = false;
}
}