C# 在 Windows 服务中使用的最佳计时器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/246697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best Timer for using in a Windows service
提问by
I need to create some windows service which will execute every N period of time.
The question is:
Which timer control should I use: System.Timers.Timer
or System.Threading.Timer
one? Does it influence on something?
我需要创建一些 Windows 服务,它将每 N 段时间执行一次。
问题是:
我应该使用哪个定时器控件:System.Timers.Timer
还是System.Threading.Timer
一个?它对某事有影响吗?
I am asking because I heard many evidences to non correct work of System.Timers.Timer
in windows services.
Thank you.
我问是因为我听到了很多关于System.Timers.Timer
Windows 服务不正确工作的证据。
谢谢你。
采纳答案by Andrew Moore
Both System.Timers.Timer
and System.Threading.Timer
will work for services.
双方System.Timers.Timer
并System.Threading.Timer
会为服务工作。
The timers you want to avoid are System.Web.UI.Timer
and System.Windows.Forms.Timer
, which are respectively for ASP applications and WinForms. Using those will cause the service to load an additional assembly which is not really needed for the type of application you are building.
您要避免的计时器是System.Web.UI.Timer
和System.Windows.Forms.Timer
,它们分别用于 ASP 应用程序和 WinForms。使用这些将导致服务加载一个额外的程序集,这对于您正在构建的应用程序类型并不是真正需要的。
Use System.Timers.Timer
like the following example (also, make sure that you use a class level variable to prevent garbage collection, as stated in Tim Robinson's answer):
System.Timers.Timer
像以下示例一样使用(另外,请确保使用类级别变量来防止垃圾收集,如 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
...
*/
If you choose System.Threading.Timer
, you can use as follows:
如果选择System.Threading.Timer
,则可以使用如下:
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();
}
}
}
Both examples comes from the MSDN pages.
这两个示例都来自 MSDN 页面。
回答by Tim Robinson
Either one should work OK. In fact, System.Threading.Timer uses System.Timers.Timer internally.
任何一个都应该可以正常工作。实际上,System.Threading.Timer 在内部使用 System.Timers.Timer。
Having said that, it's easy to misuse System.Timers.Timer. If you don't store the Timer object in a variable somewhere, then it is liable to be garbage collected. If that happens, your timer will no longer fire. Call the Dispose method to stop the timer, or use the System.Threading.Timer class, which is a slightly nicer wrapper.
话虽如此,很容易误用 System.Timers.Timer。如果您没有将 Timer 对象存储在某个变量中,那么它很可能会被垃圾收集。如果发生这种情况,您的计时器将不再触发。调用 Dispose 方法来停止计时器,或者使用 System.Threading.Timer 类,它是一个稍微好一点的包装器。
What problems have you seen so far?
到目前为止你看到了什么问题?
回答by Andrew Moore
Don't use a service for this. Create a normal application and create a scheduled task to run it.
不要为此使用服务。创建一个普通的应用程序并创建一个计划任务来运行它。
This is the commonly held best practice. Jon Galloway agrees with me. Or maybe its the other way around.Either way, the fact is that it is not best practices to create a windows service to perform an intermittent task run off a timer.
这是普遍持有的最佳实践。 乔恩·加洛韦同意我的看法。或者也许是相反的。无论哪种方式,事实是创建 Windows 服务来执行计时器运行的间歇性任务并不是最佳实践。
"If you're writing a Windows Service that runs a timer, you should re-evaluate your solution."
–Jon Galloway, ASP.NET MVC community program manager, author, part time superhero
“如果您正在编写运行计时器的 Windows 服务,您应该重新评估您的解决方案。”
–Jon Galloway,ASP.NET MVC 社区项目经理,作家,兼职超级英雄
回答by user32378
I agree with previous comment that might be best to consider a different approach. My suggest would be write a console application and use the windows scheduler:
我同意之前的评论,认为最好考虑采用不同的方法。我的建议是编写一个控制台应用程序并使用 Windows 调度程序:
This will:
这会:
- Reduce plumbing code that replicates scheduler behaviour
- Provide greater flexibility in terms of scheduling behaviour (e.g. only run on weekends) with all scheduling logic abstracted from application code
- Utilise the command line arguments for parameters without having to setup configuration values in config files etc
- Far easier to debug/test during development
- Allow a support user to execute by invoking the console application directly (e.g. useful during support situations)
- 减少复制调度程序行为的管道代码
- 通过从应用程序代码中抽象出的所有调度逻辑,在调度行为(例如仅在周末运行)方面提供更大的灵活性
- 使用命令行参数作为参数,而无需在配置文件等中设置配置值
- 在开发过程中更容易调试/测试
- 允许支持用户通过直接调用控制台应用程序来执行(例如在支持情况下有用)
回答by Nick N.
As already stated both System.Threading.Timer
and System.Timers.Timer
will work. The big difference between the two is that System.Threading.Timer
is a wrapper arround the other one.
如前所述都System.Threading.Timer
和System.Timers.Timer
正常工作。两者之间的最大区别在于System.Threading.Timer
另一个包装器周围。
System.Threading.Timer
will have more exception handling whileSystem.Timers.Timer
will swallow all the exceptions.
System.Threading.Timer
将有更多的异常处理,同时System.Timers.Timer
将吞下所有的异常。
This gave me big problems in the past so I would always use 'System.Threading.Timer' and still handle your exceptions very well.
这在过去给我带来了很大的问题,所以我总是使用“System.Threading.Timer”并且仍然可以很好地处理您的异常。
回答by MigaelM
I know this thread is a little old but it came in handy for a specific scenario I had and I thought it worth while to note that there is another reason why System.Threading.Timer
might be a good approach.
When you have to periodically execute a Job that might take a long time and you want to ensure that the entire waiting period is used between jobs or if you don't want the job to run again before the previous job has finished in the case where the job takes longer than the timer period.
You could use the following:
我知道这个线程有点旧,但它在我遇到的特定场景中派上用场,我认为值得一提的是,还有另一个原因System.Threading.Timer
可能是一个好方法。当您必须定期执行可能需要很长时间的作业并且您希望确保在作业之间使用整个等待期时,或者如果您不希望在前一个作业完成之前再次运行该作业时作业花费的时间比计时器时间长。您可以使用以下内容:
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();
}
}