如何在 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);
}
}
我在创建定时器时遇到问题,该定时器在启动时会先休眠 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);
}
}