C# - 如何暂停应用程序直到计时器完成?
C# - How to pause application until timer is finished?
我有一个需要等待特定时间的应用程序,但我还需要能够在需要时取消当前操作。我有以下代码:
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);
但是,这会造成阻塞,我将无法提出取消请求。
编辑:因此,为了详细说明 what/why,我需要暂停一下,对我的申请进行更详细的解释。基本上我想要一个定期执行 "work" 的应用程序。因此,根据下面提供的答案之一,如果我做了这样的事情:
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;
}
}
}
然后我的 service/console 应用程序将启动并进入无限循环,整天只设置计时器。以前,我实际上是在以下位置停止执行任何其他代码:
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; }
}
首先:"you absolutely do not(!) want to 'busy-wait' for anything!"(坏狗!没有饼干!)
咳咳...
这个问题的实际解决方案是在信号量(或任何其他合适的互斥对象...)上执行定时等待,而不是使用实际的计时器.如果你需要在它完成之前退出等待,只需频闪它正在等待的东西。
您现在的 "solution" 的关键问题是它将把进程猛烈地变成 100% CPU 利用率, 完全是浪费。 永远不要那样做!
您应该查看 Timer.Elapsed 事件文档。当 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
据我所知,关于 "pausing",有两个原因想暂停,我不确定哪个是你的原因:
- 您想阻止应用程序 "finishing" 正常执行和终止。
- 您想推迟执行其他代码,直到所需的间隔数结束
如果您的理由是第 2 条,那么此答案已完成。
我有一个需要等待特定时间的应用程序,但我还需要能够在需要时取消当前操作。我有以下代码:
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);
但是,这会造成阻塞,我将无法提出取消请求。
编辑:因此,为了详细说明 what/why,我需要暂停一下,对我的申请进行更详细的解释。基本上我想要一个定期执行 "work" 的应用程序。因此,根据下面提供的答案之一,如果我做了这样的事情:
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;
}
}
}
然后我的 service/console 应用程序将启动并进入无限循环,整天只设置计时器。以前,我实际上是在以下位置停止执行任何其他代码:
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; }
}
首先:"you absolutely do not(!) want to 'busy-wait' for anything!"(坏狗!没有饼干!)
咳咳...
这个问题的实际解决方案是在信号量(或任何其他合适的互斥对象...)上执行定时等待,而不是使用实际的计时器.如果你需要在它完成之前退出等待,只需频闪它正在等待的东西。
您现在的 "solution" 的关键问题是它将把进程猛烈地变成 100% CPU 利用率, 完全是浪费。 永远不要那样做!
您应该查看 Timer.Elapsed 事件文档。当 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
据我所知,关于 "pausing",有两个原因想暂停,我不确定哪个是你的原因:
- 您想阻止应用程序 "finishing" 正常执行和终止。
- 您想推迟执行其他代码,直到所需的间隔数结束
如果您的理由是第 2 条,那么此答案已完成。