用c实现定时器的另一种方法



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

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 时它就停止了

有没有其他方法可以更好地实施它?

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

首先,如果你在控制台中运行它,并且不想让回车键杀死它,你可以做一些类似的事情

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

代替

Console.ReadLine(); 

这意味着只有当您键入exit并按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();

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

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

在Windows服务中,通常会覆盖ServiceBaseOnStart()OnStop()方法。在您的情况下,服务代码如下所示:

private Timer tmr;
protected override void OnStart(string[] args)
{
    tmr = new Timer (function_to_be_repeated, "...", 5000, 1000);
}
protected override void OnStop()
{
    tmr.Dispose();
}

我不确定你遇到的问题是什么,但这里有一个使用计时器的例子,它实际上可以保护自己不与自己并行运行(默认情况下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"中更改代码。

最新更新