在c#中实现计时器的另一种方法

时间:2012-01-08 07:40:20

标签: c# windows windows-services

我必须每隔n分钟在Windows服务中运行一个函数。完成后,重新开始。如果该功能已启动,则在完成之前无法再次启动。最初我有这个想法:

void function()
{
  while(true)
  {
    if(!is_running)
    {
      function_to_be_repeated();
      is_running = false;
    }
    else
    {
      Thread.Sleep(some_time); // wait to start again
    }
  }
}

然后我发现了http://www.albahari.com/threading/part3.aspx

 bool running = false;

 static void Main()
  {
    // First interval = 5000ms; subsequent intervals = 1000ms
    Timer tmr = new Timer (function_to_be_repeated, "...", 5000, 1000);
    Console.ReadLine();    
    tmr.Dispose();         // This both stops the timer and cleans up.
  }

  static void function_to_be_repeated(object data)
  {
      if(!running)
      {
        running = true;
        // do some stuff...

        running = false;
      }

  }

但是当我按Enter

时它停止了

还有其他方法可以更好地实施吗?

3 个答案:

答案 0 :(得分:1)

当你提到一个Windows服务和一个结束你的应用程序的Enter键时,我不完全确定问题是什么,然后你提到了一个更好的实现。

首先,如果您在控制台中运行它并且不想让Enter键杀死它,您可以执行类似

的操作
do
{

}
while (!Console.ReadLine().Equals("exit",
                                   StringComparison.InvariantCultureIgnoreCase)
       );

而不是

Console.ReadLine(); 

这意味着您的控制台只会在您输入退出并按Enter键时结束,或者如果您通过按x按钮关闭程序或终止该过程。

至于更好的实现,我不确定这是否是最好的做法,但这就是我使用轮询线程实现的方式:

class Poll : IDisposable
{
    private TimeSpan polledSpan;
    WaitHandle[] handles = new WaitHandle[2];
    ManualResetEvent exit = new ManualResetEvent(false);
    Thread thread;
    public Poll(int polledTime)
    {
        polledSpan = new TimeSpan(0, 0, polledTime);
        thread = new Thread(new ThreadStart(Start));
        thread.Start();
    }

    private void Start()
    {            
        AutoResetEvent reset = new AutoResetEvent(false);
        handles[0] = reset;
        handles[1] = exit;
        bool run = true;
        while (run)
        {                
            int result = WaitHandle.WaitAny(handles, 
                                           (int)polledSpan.TotalMilliseconds, 
                                           false);

            switch(result)
            {
                case WaitHandle.WaitTimeout:
                    run = StuffToDo();
                    break;
                case 1:
                case 0:
                    run = false;
                    break;
            }                
        }            
    }

    private bool StuffToDo()
    {
        try
        {
            Console.WriteLine("Test");
            return true;
        }
        catch
        {
            return false;
        }
    }

    public void Dispose()
    {
        exit.Set();
        if (thread != null)
        {
            thread.Join(10000);
        }
        exit = null;
        handles = null;
    }
}

并在主要方法中

Poll p = new Poll(1);
do
{

}
while (!Console.ReadLine().Equals("exit",
                                  StringComparison.InvariantCultureIgnoreCase));
p.Dispose();

答案 1 :(得分:0)

上面的代码可能在控制台应用程序中运行,这就是在你按 ENTER 之后它存在的原因(更准确地说,这是因为Console.Readline())。

但是,您还提到在Windows服务中需要此功能。

在Windows服务中,您通常会覆盖OnStart()的{​​{1}}和OnStop()方法。在您的情况下,服务代码将如下所示:

ServiceBase

答案 2 :(得分:0)

我不确定你遇到了什么问题,但这里有一个使用计时器的例子,它实际上可以防止自己与自身并行运行(System.Threading.Timer默认情况下不会这样做) 。

此外,如果您想在Windows服务中运行它,只需将计时器的创建移动到start方法并进行处理以停止并将计时器实例保留在属性中。

class Program
{
    static void Main(string[] args)
    {
        var timeBetweenTicks = (long) TimeSpan.FromSeconds(1).TotalMilliseconds;
        System.Threading.Timer timer = null;
        Action onTick = () =>
            {
                timer.Change(Timeout.Infinite, Timeout.Infinite);
                try
                {
                    //Work on tick goes here
                    Console.WriteLine(DateTime.Now);
                }
                finally
                {
                    timer.Change(timeBetweenTicks, timeBetweenTicks);
                }
            };
        using (timer = new System.Threading.Timer(_ => onTick(), null, 0, timeBetweenTicks))
        {
            Console.ReadKey();
        }
    }
}

请注意,如果您希望下一个刻度的时间受当前刻度的影响,您需要添加一些时间并更改“finally”中的代码。