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

C# 在c中实现计时器的另一种方法#,c#,windows,windows-services,C#,Windows,Windows Services,在windows服务中,我必须每n分钟运行一次函数。完成后,重新开始。如果函数已启动,则在完成之前无法再次启动。最初我有一个想法: void function() { while(true) { if(!is_running) { function_to_be_repeated(); is_running = false; } else { Thread.Sleep(some_time); // wait to

在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
    }
  }
}
然后我发现:


但当我按下回车键时它就停止了


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

上面的代码可能是在控制台应用程序中运行的,这就是为什么按ENTER键后会出现它(更准确地说,这是因为
console.Readline()

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

在Windows服务中,您通常会覆盖
ServiceBase
OnStart()
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();
}

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

首先,如果您在控制台中运行它,并且不希望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();

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

“但当我按Enter键时它会停止”-是的,代码就是这样做的。现在你有问题了吗?我倾向于使用
AutoResetEvent
计时器,而不是使用
Thread.Sleep
,它工作得相当好。我不能说它是否是建议的解决方案,但它为我提供了一个更轻松的解决方案。在.NET中有许多不同的类,名为
Timer
(在不同的名称空间中)。你用哪一种?
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();
        }
    }
}