如何在时间滴答作响时暂停循环

本文关键字:暂停 循环 滴答 时间 | 更新日期: 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 线程的多线程操作的重要规则之一。