如何在时间流逝时暂停循环

时间:2015-01-11 14:05:18

标签: c#

我做了一个计时器,我可以设定时间,我想等待,然后做一些事情。 所以这是我的短计时器功能:

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()。我正在处理表单应用程序,所以所有threadin睡觉都不会在这里工作。此外,我尝试了所有其他计时器,但他们使用了太多的CPU。

3 个答案:

答案 0 :(得分:1)

最好将所有操作添加到队列中,然后在计时器结束后执行 目前只有一次您的代码建议您在每次调用Sleep方法时创建新的计时器 您正在使用的计时器也会在当前Window的主调度程序上运行,如果您想在另一个线程上指定并运行计时器,则可能需要使用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);
}

答案 1 :(得分:0)

您的循环不会停止,因为计时器实际上并未执行任何暂停当前线程执行的命令。

虽然你的例子有点模糊,但我假设你正在寻找的是Task.Delay内部使用计时器。您可以异步等待它,以便循环将控制权交还给调用者:

foreach (string word in words)
{
    await Task.Delay(5000);
    action();
}

请注意,这意味着您必须将方法标记为async Task,或者将其标记为事件处理程序async void

答案 2 :(得分:0)

它&#34;似乎&#34;喜欢它立即再次发生是因为你的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线程,因为它在一个单独的线程上运行,而你的代码会在循环时阻止用户界面。

  • 正在使用的计时器类是System.Threading.Timer,而不是System.Windows.Forms.Timer(有关.NET计时器的详细解释,请参阅this

  • 如果您想在计时器内(即在myAction代表内)对UI线程执行任何操作,则必须使用this.Invoke(myAction)执行此操作,这是重要规则之一涉及UI线程的多线程操作。