【问题标题】:another way to implement a timer in c#在 C# 中实现计时器的另一种方法
【发布时间】:2023-03-20 04:46:01
【问题描述】:

我必须在 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 时它停止了

还有其他更好的实现方式吗?

【问题讨论】:

  • “但是当我按下 Enter 时它会停止” - 是的,这就是代码的作用。现在你有问题了吗?
  • 我倾向于将AutoResetEvent 用于计时器,而不是使用Thread.Sleep,而且效果相当好。我不能说它是否是建议的解决方案,但它为我提供了一种更轻松的解决方案。
  • .NET 中有很多不同的类,名为Timer(在不同的命名空间中)。你用的是哪一个?

标签: c# windows windows-services


【解决方案1】:

当您提到 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;
    }
}

在main方法中

Poll p = new Poll(1);
do
{

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

【讨论】:

    【解决方案2】:

    上面的代码可能是在控制台应用程序中运行的,这就是你按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();
    }
    

    【讨论】:

      【解决方案3】:

      我不确定您遇到了什么问题,但这里有一个使用计时器的示例,该计时器实际上可以保护自己不与自身并行运行(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”中的代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-03
        相关资源
        最近更新 更多