如何在时间滴答作响时暂停循环
本文关键字:暂停 循环 滴答 时间 | 更新日期: 2023-09-27 18:37:02
我制作了一个计时器,我可以在其中设置我想等待的时间,然后做某事。所以这是我的简短计时器功能:
private void Sleep(int interval, Action action)
{
System.Windows.Forms.Timer mytimer = new System.Windows.Forms.Timer();
mytimer.Interval = interval; //interval is in ms
mytimer.Start();
mytimer.Tick += (s, e) =>
{
action();
mytimer.Stop();
};
}
我在循环中使用这个计时器:
foreach (string word in words)
{
Sleep(5000, ()=> myAction());
}
没有循环计时器很棒,但在循环中它将无法工作,因为循环不会停止并等待这 5 秒。它立即完成所有操作,并且一次又一次地启动计时器太快。
所以我试图找出的是如何让我的循环等到时间用完并执行myAction()。我正在处理表单应用程序,因此所有线程睡眠都无法在这里工作。我也尝试了所有其他计时器,但它们使用了太多的CPU。
操作添加到队列中,然后在计时器结束后执行。
当前只有一次,您的代码建议您每次调用 Sleep
方法时都会创建一个新的计时器。
此外,您使用的计时器在当前窗口的主调度程序上运行,如果您想在不同的线程上指定和运行计时器,
您可能希望使用DispatcherTimer
。
private Queue<Tuple<string,Action>> _queuedAction = new Queue<Tuple<string,Action>>();
然后
foreach(var word in words){
_queuedAction.Enqueue(new Tuple<string, Action>(word, ()=>{});
}
var timer= new System.Windows.Forms.Timer();
timer.Interval = interval;
timer.Elapsed += OnTimerElapsed;
timer.Start();
然后
private void OnTimerElapsed(object sender, EventArgs e){
if(!_queuedAction.Any()) return;
var tuple = _queuedAction.Dequeue();
//Execute your Action here.
var word = tuple.Item1;
var action = tuple.Item2;
action();
}
在这里,我们将操作添加到队列中,每次间隔经过时,都会调用OnTimeElapsed
方法,然后在其中执行操作。
否则,如果您正在寻找同步解决方案,我们可以简单地拥有这样的东西
foreach(var word in words){
DoSomethingWithWord(word);
Thread.Sleep(5000);
}
您的循环不会停止,因为计时器实际上并没有执行任何停止当前线程执行的命令。
虽然你的例子有点模糊,但我假设你正在寻找的是内部使用计时器Task.Delay
。您可以异步等待它,以便循环将控制权交还给调用方:
foreach (string word in words)
{
await Task.Delay(5000);
action();
}
请注意,这意味着您必须将方法标记为 async Task
,或者如果它是事件处理程序async void
。
"看起来"像是立即再次发生的原因是因为你的foreach
循环实际上没有任何延迟地发生 - 以你的处理器可以循环的速度 - 并立即调用Sleep
很多很多次,从而排队所有那些计时器,每个计时器都在等待你的5000毫秒,然后全部触发你的操作。
你可以这样做而不是你的foreach
循环,并摆脱所有其他代码(即Sleep
方法):
System.Threading.Tasks.Task t = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
var timer = new System.Threading.Timer(new System.Threading.TimerCallback(x => this.Invoke(myAction)), null, 5000, 5000);
});
请注意以下几点:
计时器不会阻止 UI 线程,因为它在单独的线程上运行,而代码会在 UI 循环时阻止它。
正在使用的计时器类是
System.Threading.Timer
,而不是System.Windows.Forms.Timer
(有关 .NET 计时器的良好解释,请参阅此处)如果要从计时器(即在
myAction
委托中)对 UI 线程执行任何操作,则必须使用this.Invoke(myAction)
执行此操作,这是涉及 UI 线程的多线程操作的重要规则之一。