在Windows服务中使用计时器

20

我有一个Windows服务,在其中我希望每10秒钟创建一个文件。

我收到很多评论说,Windows服务中的定时器是最好的选择。

我该如何实现这一点?


我不建议在Windows服务中使用任何计时器,因为异常处理不当可能会导致计时器停止,但服务不会停止。这是一件容易忘记正确异常处理的事情。 - StingyJack
未被正确处理或未处理的异常。 - StingyJack
6个回答

30

首先,选择正确类型的定时器。你需要使用 System.Timers.TimerSystem.Threading.Timer - 不要使用与UI框架相关的定时器(例如System.Windows.Forms.TimerDispatcherTimer)。

通常来说,定时器是非常简单的:

  1. 设置间隔时间
  2. Elapsed事件添加处理程序(或在构造函数中传递回调函数)
  3. 如有必要,则启动定时器(不同类的工作方式不同)

这样就可以了。

示例:

// System.Threading.Timer sample
using System;
using System.Threading;

class Test
{
    static void Main() 
    {
        TimerCallback callback = PerformTimerOperation;
        Timer timer = new Timer(callback);
        timer.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1));
        // Let the timer run for 10 seconds before the main
        // thread exits and the process terminates
        Thread.Sleep(10000);
    }

    static void PerformTimerOperation(object state)
    {
        Console.WriteLine("Timer ticked...");
    }
}

// System.Timers.Timer example
using System;
using System.Threading;
using System.Timers;
// Disambiguate the meaning of "Timer"
using Timer = System.Timers.Timer;

class Test
{
    static void Main() 
    {
        Timer timer = new Timer();
        timer.Elapsed += PerformTimerOperation;
        timer.Interval = TimeSpan.FromSeconds(1).TotalMilliseconds;
        timer.Start();
        // Let the timer run for 10 seconds before the main
        // thread exits and the process terminates
        Thread.Sleep(10000);
    }

    static void PerformTimerOperation(object sender,
                                      ElapsedEventArgs e)
    {
        Console.WriteLine("Timer ticked...");
    }
}

我在这个页面上有更多信息,尽管我已经很长时间没有更新了。


1
@PARTH:这些只是示例控制台应用程序 - 我只是在命令行上编译并运行它们。它们只是为了演示如何设置和启动计时器。通常情况下,您不会在Windows服务中使用Console.WriteLine。有很多关于编写Windows服务的一般方面的教程 - 我只是专注于启动计时器的具体任务。 - Jon Skeet
@PARTH:在Windows服务中,您可以从“开始”事件或其他事件启动计时器。这将在10秒后完成,因为主线程退出。但是,在Windows服务中不会发生这种情况。我建议您阅读有关Windows服务生命周期的更多信息。 - Jon Skeet
如果我没理解错的话,你的意思是在Windows服务中不需要使用Thread.Sleep(10000)吗? - Parth Bhatt
@PARTH:没错。这只是为了样例而已。 - Jon Skeet
1
嘿,Jon,谢谢回复。这对我有用。感谢“工作大师”(Jon Skeet)..!非常感谢 :) - Parth Bhatt
显示剩余6条评论

13

我不建议使用System.Timers.Timer,因为它会默默吃掉未处理的异常,从而隐藏了你应该修复的错误。在我看来,如果您没有正确处理异常,最好让您的代码崩溃。

至于System.Threading.Timer,我倾向于使用Change方法来启动/停止计时器,类似于以下模式:

public class MyCoolService
{
    Timer _timer;

    public MyCoolService()
    {
        _timer = new Timer(MyWorkerMethod, Timeout.Infinite, Timeout.Infinite);
    }

    protected void OnStart()
    {
        _timer.Change(15000, Timeout.Infinte);
    }

    protected void MyWorkerMethod()
    {
        //pause timer during processing so it
        // wont be run twice if the processing takes longer
        // than the interval for some reason
        _timer.Change(Timeout.Infinite, Timeout.Infinite); 

        try
        {
            DoSomeWork();
        }
        catch (Exception err)
        {
            // report the error to your manager if you dare
        }

        // launch again in 15 seconds
        _timer.Change(15000, Timeout.Infinite);
    }

}

1
"new Timer(MyWorkerMethod, Timeout.Infinite, Timeout.Infinite);" 有语法错误。 - Nick Binnet
是的,那就是System.Threading.Timer。在.NET Framework 4.0客户端配置文件上会产生错误。 - Nick Binnet
1
好的。根据文档:http://msdn.microsoft.com/en-us/library/2x96zfy7.aspx,应该是存在的。也许你需要在线程方法中使用一个 object state 参数。 - jgauffin
我也在MyWorkerMethod方法中添加了“对象状态”。 :) - Nick Binnet
1
@MrHeelis:protected void MyWorkerMethod(object state) - jgauffin
显示剩余2条评论

3

这是简单的操作步骤

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using System.IO;

  namespace MyService
{
    public partial class Service1 : ServiceBase
    {
        Timer myTimer;
        int x = 0;
        public Service1()
         {
             InitializeComponent();
         }

         protected override void OnStart(string[] args)
         {

             myTimer = new Timer(10000);                                // Sets a 10 second interval
             myTimer.Elapsed +=new ElapsedEventHandler(myTimer_Elapsed);// Specifies The Event Handler
             myTimer.Enabled = true;                                    // Enables the control
             myTimer.AutoReset = true;                                  // makes it repeat
             myTimer.Start();                                           // Starts the interval




         }
         protected void myTimer_Elapsed(object sender, ElapsedEventArgs e)
         {
             // All the Cool code that you need to run eg. Making a new file every 10 seconds
             x++;
             StreamWriter myFile = new StreamWriter("MyFile" + x.ToString() + ".txt");
             myFile.Write("Something");
             myFile.Close();
         }
         protected override void OnStop()
         {
         }
     }
 }

上面的代码是整个带有计时器的服务。我意识到这是一篇旧文章,但我花了几个小时才搞明白。希望能帮助到某个需要的人。


1
这应该只是启动一个具有正确间隔和自动重置为true的System.Timers.Timer,并处理Elapsed的情况(但要注意;回调不在任何特定线程上)。
MSDN有一个例子:http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx 还来自MSDN:
计时器组件是一种基于服务器的计时器,允许您指定应用程序中触发Elapsed事件的重复间隔。然后,您可以处理此事件以提供定期处理。例如,假设您有一个必须保持运行24小时,7天每周的关键服务器。您可以创建一个使用计时器的服务来定期检查服务器并确保系统正常运行。如果系统没有响应,服务可以尝试重新启动服务器或通知管理员。
基于服务器的计时器设计用于在多线程环境中与工作线程一起使用。服务器计时器可以在线程之间移动以处理已触发的Elapsed事件,从而比Windows计时器更准确地按时触发事件。

1

这里有一个示例,展示如何在Windows服务中使用计时器


0

完全测试过的解决方案...

 using System;
    using System.Configuration;
    using System.ServiceProcess;
    using System.Timers;

    namespace SomeServices.WindowsService
    {
        public partial class SomeServicesWindowsService : ServiceBase 
        {
            private const int DefaultTriggerInterval = 5000;

            private readonly Timer _trigger;

            public SomeServicesWindowsService ()
            {
                InitializeComponent();
                _trigger = new Timer(GetTriggerInterval());
                _trigger.Elapsed += TriggerElapsed;
            }
            public bool ContinueTriggering { get; set; }

            public void TriggerElapsed(object sender, ElapsedEventArgs e)
            {
                var subject = (Timer)sender;
                subject.Stop();

                using (var service = new DeliveryServiceManager())
                {
                    service.ShouldContinue += service_ShouldContinue;
                    service.Run();
                }

                if (ContinueTriggering)
                    subject.Start();
            }

            void service_ShouldContinue(object sender, ShouldContinueEventArgs e)
            {
                e.Continue = ContinueTriggering;
            }

            public double GetTriggerInterval()
            {
                int interval;
                return int.TryParse(ConfigurationManager.AppSettings["triggerInterval"], out interval)
                    ? interval
                    : DefaultTriggerInterval;
            }

            protected override void OnStart(string[] args)
            {
                ContinueTriggering = true;
                _trigger.Start();
            }

            protected override void OnStop()
            {
                ContinueTriggering = false;
                _trigger.Stop();
            }
        }

        public class DeliveryServiceManager : IDisposable
        {
            public event EventHandler<ShouldContinueEventArgs> ShouldContinue;
            protected virtual void OnShouldContinue(ShouldContinueEventArgs e)
            {
                var handler = ShouldContinue;
                if (handler != null)
                {
                    handler(this, e);
                }
            }

            public void Dispose()
            {
                ShouldContinue = null;
            }

            public void Run()
            {
               //Iterate Something here.
                var eventArgs = new ShouldContinueEventArgs{Continue =  false};
                OnShouldContinue(eventArgs);
                if (!eventArgs.Continue)
                {
                    //Run step();        
                }
            }
        }

        public class ShouldContinueEventArgs : EventArgs
        {
            public bool Continue { get; set; }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接