Je dois créer un service Windows qui s'exécutera toutes les N périodes de temps. La question est la suivante : quel contrôle de minuterie dois-je utiliser ? System.Timers.Timer ou System.Threading.Timer ? Cela a-t-il une influence sur quelque chose ? Je pose la question parce que j'ai entendu de nombreuses preuves du mauvais fonctionnement de System.Timers.Timer dans les services Windows. Merci.
Réponses
Trop de publicités?Les deux sites System.Timers.Timer
et System.Threading.Timer
fonctionnera pour les services.
Les minuteurs que vous voulez éviter sont System.Web.UI.Timer
et System.Windows.Forms.Timer
qui sont respectivement destinés aux applications ASP et WinForms. En les utilisant, le service chargera un assemblage supplémentaire qui n'est pas vraiment nécessaire pour le type d'application que vous construisez.
Utilisez System.Timers.Timer
comme dans l'exemple suivant (veillez également à utiliser une variable au niveau de la classe pour éviter le garbage collection, comme indiqué dans la réponse de Tim Robinson) :
using System;
using System.Timers;
public class Timer1
{
private static System.Timers.Timer aTimer;
public static void Main()
{
// Normally, the timer is declared at the class level,
// so that it stays in scope as long as it is needed.
// If the timer is declared in a long-running method,
// KeepAlive must be used to prevent the JIT compiler
// from allowing aggressive garbage collection to occur
// before the method ends. (See end of method.)
//System.Timers.Timer aTimer;
// Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 2 seconds (2000 milliseconds).
aTimer.Interval = 2000;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
// If the timer is declared in a long-running method, use
// KeepAlive to prevent garbage collection from occurring
// before the method ends.
//GC.KeepAlive(aTimer);
}
// Specify what you want to happen when the Elapsed event is
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
}
/* This code example produces output similar to the following:
Press the Enter key to exit the program.
The Elapsed event was raised at 5/20/2007 8:42:27 PM
The Elapsed event was raised at 5/20/2007 8:42:29 PM
The Elapsed event was raised at 5/20/2007 8:42:31 PM
...
*/
Si vous choisissez System.Threading.Timer
vous pouvez l'utiliser comme suit :
using System;
using System.Threading;
class TimerExample
{
static void Main()
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
StatusChecker statusChecker = new StatusChecker(10);
// Create the delegate that invokes methods for the timer.
TimerCallback timerDelegate =
new TimerCallback(statusChecker.CheckStatus);
// Create a timer that signals the delegate to invoke
// CheckStatus after one second, and every 1/4 second
// thereafter.
Console.WriteLine("{0} Creating timer.\n",
DateTime.Now.ToString("h:mm:ss.fff"));
Timer stateTimer =
new Timer(timerDelegate, autoEvent, 1000, 250);
// When autoEvent signals, change the period to every
// 1/2 second.
autoEvent.WaitOne(5000, false);
stateTimer.Change(0, 500);
Console.WriteLine("\nChanging period.\n");
// When autoEvent signals the second time, dispose of
// the timer.
autoEvent.WaitOne(5000, false);
stateTimer.Dispose();
Console.WriteLine("\nDestroying timer.");
}
}
class StatusChecker
{
int invokeCount, maxCount;
public StatusChecker(int count)
{
invokeCount = 0;
maxCount = count;
}
// This method is called by the timer delegate.
public void CheckStatus(Object stateInfo)
{
AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
Console.WriteLine("{0} Checking status {1,2}.",
DateTime.Now.ToString("h:mm:ss.fff"),
(++invokeCount).ToString());
if(invokeCount == maxCount)
{
// Reset the counter and signal Main.
invokeCount = 0;
autoEvent.Set();
}
}
}
Les deux exemples proviennent des pages MSDN.
N'utilisez pas un service pour cela. Créez une application normale et créez une tâche planifiée pour l'exécuter.
C'est la meilleure pratique communément admise. Jon Galloway est d'accord avec moi. Ou peut-être que c'est l'inverse. Quoi qu'il en soit, le fait est que ce n'est pas une bonne pratique de créer un service Windows pour effectuer une tâche intermittente exécutée par une minuterie.
"Si vous écrivez un service Windows qui exécute une minuterie, vous devriez réévaluer votre solution."
Jon Galloway, gestionnaire du programme communautaire ASP.NET MVC, auteur et super-héros à temps partiel.
L'un ou l'autre devrait fonctionner correctement. En fait, System.Threading.Timer utilise System.Timers.Timer en interne.
Cela dit, il est facile de faire un mauvais usage de System.Timers.Timer. Si vous ne stockez pas l'objet Timer dans une variable quelque part, il risque de faire l'objet d'une collecte sélective. Si cela se produit, votre timer ne se déclenchera plus. Appelez la méthode Dispose pour arrêter le timer, ou utilisez la classe System.Threading.Timer, qui est une enveloppe légèrement plus agréable.
Quels problèmes avez-vous rencontrés jusqu'à présent ?
Je suis d'accord avec le commentaire précédent : il serait peut-être préférable d'envisager une approche différente. Je suggère d'écrire une application console et d'utiliser le planificateur de Windows :
Ce sera :
- Réduire le code de plomberie qui reproduit le comportement de l'ordonnanceur.
- Offrir une plus grande flexibilité en termes de comportement de programmation (par exemple, uniquement les week-ends), toute la logique de planification étant abstraite du code de l'application.
- Utiliser les arguments de la ligne de commande pour les paramètres sans avoir à configurer les valeurs de configuration dans etc.
- Beaucoup plus facile à déboguer/tester pendant le développement
- Permettre à un utilisateur du support d'exécuter en invoquant l'application console directement (par exemple, utile dans les situations d'assistance)
Comme déjà indiqué, les deux System.Threading.Timer
et System.Timers.Timer
fonctionnera. La grande différence entre les deux est que System.Threading.Timer
est une enveloppe autour de l'autre.
System.Threading.Timer
aura plus de gestion des exceptions tandis queSystem.Timers.Timer
avalera toutes les exceptions.
Cela m'a posé de gros problèmes dans le passé, c'est pourquoi j'ai toujours utilisé 'System.Threading.Timer', tout en gérant très bien vos exceptions.