如何在 C# 控制台应用中添加计时器

就是这个——你怎么给 C# 控制台应用加个计时器?如果您能提供一些示例编码,那就太好了。

313810 次浏览

使用 System.Threading.Timer 类。

System.Windows.Forms.Timer 主要设计用于单个线程,通常用于 WindowsFormsUI 线程。

还有一个系统。类的开发早期添加的定时器类。NET 架构。不过,一般建议使用该系统。穿线。而是定时器类,因为它只是 System 的一个包装器。穿线。还有计时器。

还建议始终使用静态(在 VB.NET 中共享)系统。穿线。如果您正在开发 Windows 服务并需要定时器定期运行,则使用定时器。这将避免可能过早地对计时器对象进行垃圾回收。

下面是一个定时控制台应用的例子:

using System;
using System.Threading;
public static class Program
{
public static void Main()
{
Console.WriteLine("Main thread: starting a timer");
Timer t = new Timer(ComputeBoundOp, 5, 0, 2000);
Console.WriteLine("Main thread: Doing other work here...");
Thread.Sleep(10000); // Simulating other work (10 seconds)
t.Dispose(); // Cancel the timer now
}
// This method's signature must match the TimerCallback delegate
private static void ComputeBoundOp(Object state)
{
// This method is executed by a thread pool thread
Console.WriteLine("In ComputeBoundOp: state={0}", state);
Thread.Sleep(1000); // Simulates other work (1 second)
// When this method returns, the thread goes back
// to the pool and waits for another task
}
}

摘自 Jeff Richter 的书 CLR Via C # 。顺便说一下,这本书在第23章中描述了3种计时器背后的基本原理,强烈推荐。

下面是创建一个简单的一秒计时器滴答的代码:

  using System;
using System.Threading;


class TimerExample
{
static public void Tick(Object stateInfo)
{
Console.WriteLine("Tick: {0}", DateTime.Now.ToString("h:mm:ss"));
}


static void Main()
{
TimerCallback callback = new TimerCallback(Tick);


Console.WriteLine("Creating timer: {0}\n",
DateTime.Now.ToString("h:mm:ss"));


// create a one second timer tick
Timer stateTimer = new Timer(callback, null, 0, 1000);


// loop here forever
for (; ; )
{
// add a sleep for 100 mSec to reduce CPU usage
Thread.Sleep(100);
}
}
}

结果如下:

    c:\temp>timer.exe
Creating timer: 5:22:40


Tick: 5:22:40
Tick: 5:22:41
Tick: 5:22:42
Tick: 5:22:43
Tick: 5:22:44
Tick: 5:22:45
Tick: 5:22:46
Tick: 5:22:47

编辑: 将硬自旋循环添加到代码中从来都不是一个好主意,因为它们毫无益处地消耗 CPU 周期。在这种情况下,添加循环只是为了阻止应用程序关闭,允许观察线程的操作。但是,为了正确性和减少 CPU 使用,在该循环中添加了一个简单的 Sleep 调用。

如果需要更多的控制,也可以使用自己的计时机制,但可能会降低准确性和增加代码/复杂性,但我仍然建议使用计时器。不过,如果你需要控制实际的计时线程,可以这样做:

private void ThreadLoop(object callback)
{
while(true)
{
((Delegate) callback).DynamicInvoke(null);
Thread.Sleep(5000);
}
}

将是您的计时线程(修改它以在需要时停止,并在任何您想要的时间间隔)。

使用/开始你可以做:

Thread t = new Thread(new ParameterizedThreadStart(ThreadLoop));


t.Start((Action)CallBack);

Callback 是希望在每个时间间隔调用的 void 无参数方法。例如:

private void CallBack()
{
//Do Something.
}

这非常好,但是为了模拟时间流逝,我们需要运行一个需要花费一些时间的命令,这在第二个示例中非常清楚。

然而,使用 for 循环来永久执行某些功能的风格需要大量的设备资源,相反,我们可以使用垃圾收集器来执行类似的操作。

我们可以在同一本书 CLR Via C # Third Ed 中的代码中看到这种修改。

using System;
using System.Threading;


public static class Program
{
private Timer _timer = null;
public static void Main()
{
// Create a Timer object that knows to call our TimerCallback
// method once every 2000 milliseconds.
_timer = new Timer(TimerCallback, null, 0, 2000);
// Wait for the user to hit <Enter>
Console.ReadLine();
}


private static void TimerCallback(Object o)
{
// Display the date/time when this method got called.
Console.WriteLine("In TimerCallback: " + DateTime.Now);
}
}

或者使用 Rx,简短而甜蜜:

static void Main()
{
Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(t => Console.WriteLine("I am called... {0}", t));


for (; ; ) { }
}

您也可以创建自己的(如果对可用的选项不满意)。

创建自己的 Timer实现是非常基本的工作。

这是一个应用程序的示例,该应用程序需要在与我的代码库的其余部分相同的线程上访问 COM 对象。

/// <summary>
/// Internal timer for window.setTimeout() and window.setInterval().
/// This is to ensure that async calls always run on the same thread.
/// </summary>
public class Timer : IDisposable {


public void Tick()
{
if (Enabled && Environment.TickCount >= nextTick)
{
Callback.Invoke(this, null);
nextTick = Environment.TickCount + Interval;
}
}


private int nextTick = 0;


public void Start()
{
this.Enabled = true;
Interval = interval;
}


public void Stop()
{
this.Enabled = false;
}


public event EventHandler Callback;


public bool Enabled = false;


private int interval = 1000;


public int Interval
{
get { return interval; }
set { interval = value; nextTick = Environment.TickCount + interval; }
}


public void Dispose()
{
this.Callback = null;
this.Stop();
}


}

您可以添加以下事件:

Timer timer = new Timer();
timer.Callback += delegate
{
if (once) { timer.Enabled = false; }
Callback.execute(callbackId, args);
};
timer.Enabled = true;
timer.Interval = ms;
timer.Start();
Window.timers.Add(Environment.TickCount, timer);

为了确保计时器正常工作,你需要创建一个无止境的循环,如下所示:

while (true) {
// Create a new list in case a new timer
// is added/removed during a callback.
foreach (Timer timer in new List<Timer>(timers.Values))
{
timer.Tick();
}
}

让我们找点乐子

using System;
using System.Timers;


namespace TimerExample
{
class Program
{
static Timer timer = new Timer(1000);
static int i = 10;


static void Main(string[] args)
{
timer.Elapsed+=timer_Elapsed;
timer.Start(); Console.Read();
}


private static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
i--;


Console.Clear();
Console.WriteLine("=================================================");
Console.WriteLine("                  DEFUSE THE BOMB");
Console.WriteLine("");
Console.WriteLine("                Time Remaining:  " + i.ToString());
Console.WriteLine("");
Console.WriteLine("=================================================");


if (i == 0)
{
Console.Clear();
Console.WriteLine("");
Console.WriteLine("==============================================");
Console.WriteLine("         B O O O O O M M M M M ! ! ! !");
Console.WriteLine("");
Console.WriteLine("               G A M E  O V E R");
Console.WriteLine("==============================================");


timer.Close();
timer.Dispose();
}


GC.Collect();
}
}
}

医生

就是这样:)

public static void Main()
{
SetTimer();


Console.WriteLine("\nPress the Enter key to exit the application...\n");
Console.WriteLine("The application started at {0:HH:mm:ss.fff}", DateTime.Now);
Console.ReadLine();
aTimer.Stop();
aTimer.Dispose();


Console.WriteLine("Terminating the application...");
}


private static void SetTimer()
{
// Create a timer with a two second interval.
aTimer = new System.Timers.Timer(2000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}


private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
e.SignalTime);
}

在 C # 5.0 + 和.NET Framework 4.5 + 中,你可以使用异步/等待:

async void RunMethodEvery(Action method, double seconds)
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(seconds));
method();
}
}

我建议您遵循 Microsoft 指南( Https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1.

我第一次尝试使用 System.Threading;

var myTimer = new Timer((e) =>
{
// Code
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));


但是在大约20分钟后它就停止了。

有了这个,我尝试了解决方案设置

GC.KeepAlive(myTimer)

或者

for (; ; ) { }
}

但在我的案子里不起作用。

根据微软的文档,它完美地工作了:

using System;
using System.Timers;


public class Example
{
private static Timer aTimer;


public static void Main()
{
// Create a timer and set a two second interval.
aTimer = new System.Timers.Timer();
aTimer.Interval = 2000;


// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;


// Have the timer fire repeated events (true is the default)
aTimer.AutoReset = true;


// Start the timer
aTimer.Enabled = true;


Console.WriteLine("Press the Enter key to exit the program at any time... ");
Console.ReadLine();
}


private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
}
// The example displays output like the following:
//       Press the Enter key to exit the program at any time...
//       The Elapsed event was raised at 5/20/2015 8:48:58 PM
//       The Elapsed event was raised at 5/20/2015 8:49:00 PM
//       The Elapsed event was raised at 5/20/2015 8:49:02 PM
//       The Elapsed event was raised at 5/20/2015 8:49:04 PM
//       The Elapsed event was raised at 5/20/2015 8:49:06 PM

https://github.com/bigabdoul/PowerConsole使用 Github 上的 PowerConsole 项目或在 https://www.nuget.org/packages/PowerConsole上使用相应的 NuGet 包。它以可重用的方式优雅地处理计时器。请看下面的示例代码:

using PowerConsole;


namespace PowerConsoleTest
{
class Program
{
static readonly SmartConsole MyConsole = SmartConsole.Default;


static void Main()
{
RunTimers();
}


public static void RunTimers()
{
// CAUTION: SmartConsole is not thread safe!
// Spawn multiple timers carefully when accessing
// simultaneously members of the SmartConsole class.


MyConsole.WriteInfo("\nWelcome to the Timers demo!\n")


// SetTimeout is called only once after the provided delay and
// is automatically removed by the TimerManager class
.SetTimeout(e =>
{
// this action is called back after 5.5 seconds; the name
// of the timer is useful should we want to clear it
// before this action gets executed
e.Console.Write("\n").WriteError("Time out occured after 5.5 seconds! " +
"Timer has been automatically disposed.\n");


// the next statement will make the current instance of
// SmartConsole throw an exception on the next prompt attempt
// e.Console.CancelRequested = true;


// use 5500 or any other value not multiple of 1000 to
// reduce write collision risk with the next timer
}, millisecondsDelay: 5500, name: "SampleTimeout")


.SetInterval(e =>
{
if (e.Ticks == 1)
{
e.Console.WriteLine();
}


e.Console.Write($"\rFirst timer tick: ", System.ConsoleColor.White)
.WriteInfo(e.TicksToSecondsElapsed());


if (e.Ticks > 4)
{
// we could remove the previous timeout:
// e.Console.ClearTimeout("SampleTimeout");
}


}, millisecondsInterval: 1000, "EverySecond")


// we can add as many timers as we want (or the computer's resources permit)
.SetInterval(e =>
{
if (e.Ticks == 1 || e.Ticks == 3) // 1.5 or 4.5 seconds to avoid write collision
{
e.Console.WriteSuccess("\nSecond timer is active...\n");
}
else if (e.Ticks == 5)
{
e.Console.WriteWarning("\nSecond timer is disposing...\n");


// doesn't dispose the timer
// e.Timer.Stop();


// clean up if we no longer need it
e.DisposeTimer();
}
else
{
System.Diagnostics.Trace.WriteLine($"Second timer tick: {e.Ticks}");
}
}, 1500)
.Prompt("\nPress Enter to stop the timers: ")
            

// makes sure that any remaining timer is disposed off
.ClearTimers()


.WriteSuccess("Timers cleared!\n");
}
}
}

你可以使用 StopWatch类,这里有一个例子

StopWatch stopwatch = new Stopwatch();
// creating a new stopwatch class
stopwatch.Start();
// starting the stopwatch
Thread.Sleep(10000);
// waiting for 10 seconds


TimeSpan timespan = stopwatch.Elapsed;
/* creating a new timespan class and concacting it with the elapsed of the
stopwatch class */
string time = String.Format("{0:00}:{1:00}:{2:00}",
timespan.Hours, timespan.Minutes, timespan.Seconds
);


Console.Write($"The time right now is {time}");


Console.ReadKey();