如何在 C# 中创建一个以延迟和秒表开始的计时器

How do you create a timer that starts with a delay and with stopwatch in C#

我在创建定时器时遇到问题,该定时器在启动时会先休眠 2 分钟。它将 运行 总共持续 5 分钟,每一分钟都会起作用。这是我的:

class Program{
    static System.Timers.Timer aTimer;
    static System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    static TimeSpan LongToRun = new TimeSpan(0,300,0)); //run for 5 minutes

    static void Main (string[] args){
        Thread.Sleep(120000); //sleep for 2 mins
        sw.Reset();
        sw.Start();
        Start();
    }

    static void Start()
    {
        aTimer = new System.Timers.Timer();
        aTimer.Interval = 60000;  //every min
        aTimer.Enabled = true;
        aTimer.Elapsed += new ElapsedEventHandler(timer_Elapsed); 
        aTimer.Start();

    }

    static void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        TimeSpan ts = sw.Elapsed;
        if (ts.TotalSeconds >= LongToRun.TotalSeconds)
        {
            aTimer.Stop();
            return;
        }

        DoWork();
    }
}   

timer_Elapsed never is called...我做错了吗?

首先,您设置的时间段有误:

static TimeSpan LongToRun = new TimeSpan(0,**5**,0)); //run for 5 minutes

timer_Elapsed 永远不会被调用,因为您的应用程序已提前关闭。在末尾添加 Console.ReadLine()。

static void Main (string[] args){
        Thread.Sleep(120000); //sleep for 2 mins
        sw.Reset();
        sw.Start();
        Start();
        Console.ReadLine();
    }

您的启动程序 returns 在它启动计时器后立即 returns 到您的主程序。主程序结束,仍然是 运行 的计时器在它过去之前完成。

解决方案:确保您的程序 运行 足够长,让您的计时器计时结束。

  • 主程序在启动定时器
  • 之前创建一个System.Threading.ManualResetEvent
  • 启动计时器后,它等待使用 ManualResetEvent.WaitOne()
  • 设置 ManualResetEvent
  • 在您的 timer_elapsed 函数结束时,该函数使用 ManualResetEvent.Set
  • 引发事件
  • 主程序中等待此事件的线程继续处理。

另一个问题是您在启动计时器后订阅了事件。这可能不是问题的原因,但整洁的代码是在启动计时器之前订阅。

顺便说一下,计时器 class 实现了 IDisposable。这意味着此 class 的设计者通知您他使用了需要尽快释放的稀缺资源。考虑在不再需要时尽快处理定时器 class。 using (...) 语句对此非常理想。

使用 async-await 将使您的代码更易于阅读和维护。

考虑以下代码:

public static void Main()
{
    Thread.Sleep(TimeSpan.FromMinutes(2));
    var myTask = Task.Run( () => WorkerTask())
    Task.Wait(myTask);
}

private static TimeSpan longToRun = new TimeSpan.FromMinutes(5);
// easier to read than new TimeSpan(0,300,0));

public static async Task WorkerTask()
{
    StopTime = DateTime.Now + longToRun;

    // repeatedly wait a while and DoWork():
    while (DateTime.Now < StopTime)
    {
        await Task.Delay(TimeSpan.FromMinutes(2);
        DoWork();
    }
}

就是这样,不需要 ManualResetEvents,不需要秒表,不需要计时器,也不需要 timer_elapsed 事件处理程序。

将参数传递给过程 WorkerTask() 甚至取消事件都非常容易,因为如果您创建 CancellationTokenSource 并将 CancellationToken 作为参数传递给 WorkerTask,操作员按下 < esc > 就很容易。事实上,这会使代码更简单:

public static void Main()
{
    Thread.Sleep(TimeSpan.FromMinutes(2));
    using (var tokenSource = new CancellationTokenSource())
    {
        var myTask = Task.Run( () => WorkerTask(tokenSource.Token))
        tokenSource.CancelAfter(TimerSpan.FromMinutes(5));
        // instead of LongToRun
        Task.Wait(myTask);
    }
}

public static async Task WorkerTask(CancellationToken token)
{
    // repeatedly wait a while and DoWork():
    while (!token.IsCancellationRequested)
    {
        await Task.Delay(TimeSpan.FromMinutes(2, token);
        DoWork();
    }
}

更多信息:

正如谢尔盖所​​指出的,Timespan LongToRun 中设置的时间段有误。我试过只有 Thread.sleep 功能的鳕鱼。希望代码对你有帮助。

class Program {
    static System.Timers.Timer aTimer;
    static System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    static TimeSpan LongToRun = new TimeSpan(0,5,0); //run for 5 minutes

    static void Main (string[] args)    {
        Console.WriteLine("Start Of Program" + DateTime.Now);
        Thread.Sleep(120000); //sleep for 2 mins
        sw.Reset();
        sw.Start();
        Start();
    }

    static void Start() {
        TimeSpan ts = sw.Elapsed;
        while (ts.TotalSeconds < LongToRun.TotalSeconds) {
            doWork();
            Thread.Sleep(60000);
            ts = sw.Elapsed;
        }
        Console.WriteLine("End of program");
        Console.ReadLine();
    }

    static void doWork() {
         Console.WriteLine(DateTime.Now);
    }
}