Ich muss einen Windows-Dienst erstellen, der alle N-Zeiträume ausgeführt wird.
Die Frage ist:
Welche Zeitsteuerung sollte ich verwenden: System.Timers.Timer
oder System.Threading.Timer
eine? Beeinflusst es etwas?
Ich frage, weil ich viele Hinweise auf nicht korrekte Arbeit von System.Timers.Timer
in Windows-Diensten gehört habe.
Vielen Dank.
Beide System.Timers.Timer
und System.Threading.Timer
funktionieren für Dienste.
Die Timer, die Sie vermeiden möchten, sind System.Web.UI.Timer
und System.Windows.Forms.Timer
, die jeweils für ASP - Anwendungen und WinForms gelten. Wenn Sie diese verwenden, lädt der Dienst eine zusätzliche Assembly, die für den von Ihnen erstellten Anwendungstyp nicht wirklich benötigt wird.
Verwenden Sie System.Timers.Timer
wie im folgenden Beispiel (stellen Sie außerdem sicher, dass Sie eine Klassenebenenvariable verwenden, um die Speicherbereinigung zu verhindern, wie in der Antwort von Tim Robinson angegeben):
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
...
*/
Wenn Sie System.Threading.Timer
wählen, können Sie Folgendes verwenden:
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();
}
}
}
Beide Beispiele stammen von den MSDN-Seiten.
Verwenden Sie dafür keinen Dienst. Erstellen Sie eine normale Anwendung und erstellen Sie eine geplante Aufgabe, um sie auszuführen.
Dies ist die allgemein bewährte Praxis. Jon Galloway stimmt mir zu. Oder vielleicht ist es andersherum. In jedem Fall ist es eine Tatsache, dass es nicht empfohlen wird, einen Windows-Dienst zu erstellen, um eine zeitweilige Task auszuführen, bei der ein Timer ausgeführt wird.
"Wenn Sie einen Windows-Dienst schreiben, der einen Timer ausführt, sollten Sie Ihre Lösung erneut bewerten."
–Jon Galloway, ASP.NET-MVC-Community-Programmmanager, Autor, Teilzeit-Superheld
Entweder sollte man gut funktionieren. Tatsächlich verwendet System.Threading.Timer intern System.Timers.Timer.
Trotzdem ist es einfach, System.Timers.Timer zu missbrauchen. Wenn Sie das Timer-Objekt nicht irgendwo in einer Variablen speichern, besteht die Gefahr, dass Müll gesammelt wird. In diesem Fall wird Ihr Timer nicht mehr ausgelöst. Rufen Sie die Dispose-Methode auf, um den Timer zu stoppen, oder verwenden Sie die Klasse System.Threading.Timer, die einen etwas schöneren Wrapper darstellt.
Welche Probleme hast du bisher gesehen?
Ich stimme dem vorherigen Kommentar zu, der sich am besten für einen anderen Ansatz eignet. Mein Vorschlag wäre, eine Konsolenanwendung zu schreiben und den Windows-Scheduler zu verwenden:
Dieser Wille:
Wie bereits erwähnt, funktionieren sowohl System.Threading.Timer
als auch System.Timers.Timer
. Der große Unterschied zwischen den beiden ist, dass System.Threading.Timer
ein Wrapper um den anderen ist.
System.Threading.Timer
hat mehr Ausnahmebehandlung, währendSystem.Timers.Timer
schluckt alle Ausnahmen.
Dies gab mir in der Vergangenheit große Probleme, daher würde ich immer 'System.Threading.Timer' verwenden und trotzdem mit Ihren Ausnahmen sehr gut umgehen.
Ich weiß, dass dieser Thread ein wenig alt ist, aber für ein bestimmtes Szenario, das ich hatte, sehr praktisch war, und ich dachte, es lohnt sich zu beachten, dass es einen anderen Grund gibt, warum System.Threading.Timer
ein guter Ansatz ist. Wenn Sie regelmäßig einen Job ausführen müssen, der möglicherweise lange Zeit in Anspruch nimmt, und Sie möchten sicherstellen, dass die gesamte Wartezeit zwischen den Jobs verwendet wird oder wenn Sie nicht möchten, dass der Job erneut ausgeführt wird, bevor der vorherige Job abgeschlossen ist der Fall, in dem der Job länger als die Zeitspanne dauert. Sie könnten Folgendes verwenden:
using System;
using System.ServiceProcess;
using System.Threading;
public partial class TimerExampleService : ServiceBase
{
private AutoResetEvent AutoEventInstance { get; set; }
private StatusChecker StatusCheckerInstance { get; set; }
private Timer StateTimer { get; set; }
public int TimerInterval { get; set; }
public CaseIndexingService()
{
InitializeComponent();
TimerInterval = 300000;
}
protected override void OnStart(string[] args)
{
AutoEventInstance = new AutoResetEvent(false);
StatusCheckerInstance = new StatusChecker();
// Create the delegate that invokes methods for the timer.
TimerCallback timerDelegate =
new TimerCallback(StatusCheckerInstance.CheckStatus);
// Create a timer that signals the delegate to invoke
// 1.CheckStatus immediately,
// 2.Wait until the job is finished,
// 3.then wait 5 minutes before executing again.
// 4.Repeat from point 2.
Console.WriteLine("{0} Creating timer.\n",
DateTime.Now.ToString("h:mm:ss.fff"));
//Start Immediately but don't run again.
StateTimer = new Timer(timerDelegate, AutoEventInstance, 0, Timeout.Infinite);
while (StateTimer != null)
{
//Wait until the job is done
AutoEventInstance.WaitOne();
//Wait for 5 minutes before starting the job again.
StateTimer.Change(TimerInterval, Timeout.Infinite);
}
//If the Job somehow takes longer than 5 minutes to complete then it wont matter because we will always wait another 5 minutes before running again.
}
protected override void OnStop()
{
StateTimer.Dispose();
}
}
class StatusChecker
{
public StatusChecker()
{
}
// This method is called by the timer delegate.
public void CheckStatus(Object stateInfo)
{
AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
Console.WriteLine("{0} Start Checking status.",
DateTime.Now.ToString("h:mm:ss.fff"));
//This job takes time to run. For example purposes, I put a delay in here.
int milliseconds = 5000;
Thread.Sleep(milliseconds);
//Job is now done running and the timer can now be reset to wait for the next interval
Console.WriteLine("{0} Done Checking status.",
DateTime.Now.ToString("h:mm:ss.fff"));
autoEvent.Set();
}
}