C# - 如何暂停应用程序直到计时器完成?
Posted
技术标签:
【中文标题】C# - 如何暂停应用程序直到计时器完成?【英文标题】:C# - How to pause application until timer is finished? 【发布时间】:2016-11-10 06:00:25 【问题描述】:我有一个需要等待特定时间的应用程序,但如果需要,我还需要能够取消当前操作。我有以下代码:
private void waitTimer(int days)
TimeSpan waitTime = TimeSpan.FromDays(days);
System.Timers.Timer timer = new System.Timers.Timer(waitTime.TotalMilliseconds); // Wait for some number of milliseconds
timer.Enabled = true;
timer.Start();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested); // Loop forever untill timer is finished or operation is cancled.
timer.Elapsed -= new ElapsedEventHandler(OnTimedEvent); // Unsubscribe
DoWork(); // Do work when timer finishes.......
下面是定时器完成事件的事件处理程序:
private void OnTimedEvent(object obj, ElapsedEventArgs e)
TimerSettings.TimerFinished = true;
while 循环只会无限循环,直到计时器完成或发出取消请求。我想保留此功能,但我不想在等待计时器完成时永远循环。我的计时器可以设置为以多天的间隔运行,因此循环这么长时间没有意义。
还有其他方法吗?
我知道我能做到:
Thread.Sleep(runDuration.TotalMilliseconds);
但是,这会阻塞,我将无法提出取消请求。
编辑:因此,为了详细说明我需要在这里暂停什么/为什么需要对我的应用程序进行更详细的说明。基本上我想要一个定期执行“工作”的应用程序。因此,根据下面提供的答案之一,如果我做了这样的事情:
class Program
// Do something in this method forever on a regular interval
//(could be every 5min or maybe every 5days, it's up to the user)
static void Main(string[] args)
while(true)
if(args?.Length > 0)
waitTimer(args[0]);
else
wiatTimer(TimeSpan.FromDays(1).TotalSeconds); // Default to one day interval
private void waitTimer(int numIntervals)
this.ElapsedIntervals = 0;
this.IntervalsRequired = numIntervals;
this.timer = new System.Timers.Timer(1000); // raise the elapsed event every second
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
//timer.Enabled = true; timer.Start() does this for you, don't do this
timer.Start();
//thats all here
private void OnTimedEvent(object obj, ElapsedEventArgs e)
this.ElapsedIntervals += 1;
if(this.CancelRequested)
this.ElapsedIntervals = 0;
this.timer.Stop();
return;
if(this.ElapsedIntervals >= this.IntervalsRequired)
this.ElapsedIntervals = 0;
this.timer.Stop();
DoWork(); // This is where my work gets done.......
return;
然后我的服务/控制台应用程序将启动并进入一个无限循环,整天设置计时器。以前,我实际上是在以下位置停止执行任何其他代码:
while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested);
这至少有效,但如前所述,可能是暂停线程的资源密集型方式。基本上我真正需要的是一种方法来阻止我的线程,直到计时器结束。
EDIT2:这是我的最终实现,使用等待句柄似乎对我有用...
class TimerClass
/// <summary>
/// Initialize new timer. To set timer duration,
/// either set the "IntervalMinutes" app config
/// parameter, or pass in the duration timespan.
/// </summary>
/// <param name="time"></param>
internal bool StartTimer(CancellationToken quitToken, TimeSpan? duration = null)
TimeSpan runDuration = new TimeSpan();
runDuration = duration == null ? GetTimerSpan() : default(TimeSpan);
if (runDuration != default(TimeSpan))
WaitTimer(runDuration); // Waits for the runduration to pass
return true;
/// <summary>
/// Get duration to run the timer for.
/// </summary>
internal TimeSpan GetTimerSpan()
TimerSettings.Mode = App.Settings.Mode;
DateTime scheduledTime = new DateTime();
switch (TimerSettings.Mode)
case "Daily":
scheduledTime = DateTime.ParseExact(App.Settings.ScheduledTime, "HH:mm:ss", CultureInfo.InvariantCulture);
if (scheduledTime > DateTime.Now)
TimerSettings.TimerInterval = scheduledTime - DateTime.Now;
else
TimerSettings.TimerInterval = (scheduledTime + TimeSpan.FromDays(1)) - DateTime.Now;
break;
case "Interval":
double IntervalMin = double.TryParse(App.Settings.PollingIntervalMinutes, out IntervalMin) ? IntervalMin : 15.00;
int IntervalSec = Convert.ToInt32(Math.Round(60 * IntervalMin));
TimeSpan RunInterval = new TimeSpan(0, 0, IntervalSec);
TimerSettings.TimerInterval = RunInterval;
break;
case "Manual":
TimerSettings.TimerInterval = TimeSpan.FromMilliseconds(0);
break;
default:
TimerSettings.TimerInterval = (DateTime.Today + TimeSpan.FromDays(1)) - DateTime.Now;
break;
return TimerSettings.TimerInterval;
/// <summary>
/// Event handler for each timer tick.
/// </summary>
/// <param name="obj"></param>
/// <param name="e"></param>
private void OnTimedEvent(object obj, ElapsedEventArgs e)
ElapsedIntervals += 1;
if (CancelRequested.IsCancellationRequested) // If the application was cancled
ElapsedIntervals = 0;
timer.Stop();
WaitHandle.Set();
return;
if (ElapsedIntervals >= IntervalsRequired) // If time is up
ElapsedIntervals = 0;
timer.Stop();
WaitHandle.Set();
return;
/// <summary>
/// Timer method to wait for a
/// specified duration to pass.
/// </summary>
/// <param name="span"></param>
private void WaitTimer(TimeSpan span)
WaitHandle = new AutoResetEvent(false);
int tickDuration = 1000; // Number of milliseconds for each tick
IntervalsRequired = Convert.ToInt64(span.TotalMilliseconds / (tickDuration > 0 ? tickDuration : 0.01));
timer = new System.Timers.Timer(tickDuration); // Raise the elapsed event every tick
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler for when each tick is complete
timer.Start(); // Start ticking
WaitHandle.WaitOne(); // Halt the main thread untill span is reached
// Timer parameters:
private static long ElapsedIntervals get; set;
private static long IntervalsRequired get; set;
private static System.Timers.Timer timer get; set;
private static CancellationToken CancelRequested get; set;
private static string Mode get; set;
private static TimeSpan TimerInterval get; set;
private static EventWaitHandle WaitHandle get; set;
internal static class TimerSettings
internal static string Mode get; set;
internal static TimeSpan TimerInterval get; set;
【问题讨论】:
你应该详细说明你想“暂停”什么以及为什么。 如一个答案中所述,繁忙的循环是糟糕。也许用Thread.Sleep()
减慢它会有所帮助,但只是一点点,而且没有必要。您可以正确毫无困难地做到这一点。请参阅标记的副本。您可以Wait()
如接受的答案所示,或者(甚至更好)使用async
方法和await
Task
。
【参考方案1】:
您应该查看 Timer.Elapsed Event 文档。当 AutoReset 属性设置为 true(这是默认设置)时,每次经过间隔时都会重复引发此事件。我会自己计算已经过去的时间间隔,并将其与此事件处理程序中所需的时间间隔进行比较,以检查是否到了停止计时器的时间。在这种情况下,您还可以处理取消。如果您的计时器完成了所需的间隔数,您可以从该事件处理程序中调用您的 doWork 函数。
private void waitTimer(int numIntervals)
this.ElapsedIntervals = 0;
this.IntervalsRequired = numIntervals;
this.timer = new System.Timers.Timer(1000); // raise the elapsed event every second
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
//timer.Enabled = true; timer.Start() does this for you, don't do this
timer.Start();
//thats all here
private void OnTimedEvent(object obj, ElapsedEventArgs e)
this.ElapsedIntervals += 1;
if(this.CancelRequested)
this.ElapsedIntervals = 0;
this.timer.Stop();
return;
if(this.ElapsedIntervals >= this.IntervalsRequired)
this.ElapsedIntervals = 0;
this.timer.Stop();
DoWork();
return;
https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx
在我看来,关于“暂停”,想要暂停的原因有两个,我不确定你的原因是什么:
-
您希望阻止应用程序“完成”执行并正常终止。
您想推迟执行其他代码,直到所需的间隔数结束
如果你的理由是#2,那么这个答案是完整的。
【讨论】:
这是一个 Windows 服务。我喜欢你的计时器实现,但我的应用程序没有暂停。 那么您需要“暂停”应用程序的原因是什么?将相关函数中的最后一行代码作为对 waitTimer 的调用,然后通过从 Elapsed 事件处理程序中调用适当的 DoWork 函数来完成剩余的工作不是很好吗? 我明白你的意思,但根据我的应用程序的流程,在 waitTimer 方法中的timer.Start();
行之后,应用程序将继续执行。所以说我有一个循环运行 10 次的进程。在循环一,我打电话给 waitTimer(10)
。我的预期行为是等待十秒钟然后 doWork()。实际发生的是 waitTimer() 连续调用 10 次,而 doWork() 在最初的 10 秒过去后一次发生 10 次!我希望 doWork 每 10 秒发生一次。
另一个例子是,为了举例,这是一个控制台应用程序。如果我的控制台应用程序只做一件事怎么办:即在 Main 方法中调用 waitTimer(10)。应用程序不会打开,调用 waitTimer() 然后立即关闭吗?它永远不会持续运行足够长的时间让 OnTimedEvent() 触发,除非我再次放入一些“忙等待”循环或 thread.sleep()。
我建议您使用普通的控制台应用程序并将其作为计划任务运行,而不是在服务中使用计时器。 weblogs.asp.net/jongalloway/428303【参考方案2】:
首先:“你绝对不(!)想要'忙-等待'任何事情!”(BAD Dog!NO Biscuit!) p>
咳咳……
此问题的一个实际解决方案是在信号量(或任何其他合适的互斥对象...)上执行定时等待,而不是使用实际的计时器。如果您需要在等待完成之前退出等待,只需选通它正在等待的对象即可。
您目前的“解决方案”的关键问题是它会将进程猛烈撞击到100% CPU 利用率, 完全是浪费。 永远不要那样做!
【讨论】:
“你目前的“解决方案”的关键问题是它会将进程猛烈投入 100% CPU”,是的,但是Thread.Sleep(100)
或他的 while 循环中的适当值将解决这个问题(而不是而不是全速旋转)。另外,很高兴看到您的“实用解决方案”,因为尽管阅读了您的答案,但我不知道如何实现它,而是将 Sleep
添加到 OP 的代码中。
支持您的评论,因为在现实世界中,这很可能就足够了。 “不管怎样,'足够好和平工作,'”正如我已故的叔叔曾经说过的那样。在原本繁忙的等待循环中进行“100 毫秒的睡眠”确实可以回避问题,我完全赞成“任何可行的方法”。
我同意评论表明这个答案在没有代码示例的情况下不是很有用。坦率地说,这个问题已经被问过很多次了,所以我真的不认为我们在 Stack Overflow 上需要另一个答案。但如果你要提供一个,它应该是一个很好的。对SemaphoreSlim
、ManualResetEvent
等进行定时等待并不是一个坏建议,但考虑到当前的 TPL 功能,这有点过时了。
跟 OP 说话就像跟狗说话?严重地?忽略忙碌等待有其实际用例这一事实,因为它具有最低的延迟。以上是关于C# - 如何暂停应用程序直到计时器完成?的主要内容,如果未能解决你的问题,请参考以下文章