C# 如何使用定时器等待?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14126035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 10:44:24  来源:igfitidea点击:

How to use a timer to wait?

c#winformstimersleep

提问by Fuzz Evans

I am trying to delay events in my method by using a timer, however i do not necessarily understand how to use a timer to wait.

我试图通过使用计时器来延迟我的方法中的事件,但是我不一定了解如何使用计时器进行等待。

I set up my timer to be 2 seconds, but when i run this code the last call runs without a 2 second delay.

我将计时器设置为 2 秒,但是当我运行此代码时,最后一次调用的运行没有 2 秒的延迟。

Timer timer = new Timer();
timer.Tick += new EventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000) * (2);              // Timer will tick evert second
timer.Enabled = true;                       // Enable the timer


void timer_Tick(object sender, EventArgs e)
{
    timer.Stop();
}

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    timer.Start();
    label1.Text = "second";
}

So when i click my button, it immediately shows label1 as "second", as opposed to changing to "first", waiting 2 seconds, then changing to "second". I have read lots of threads here about using timers instead of thread.sleep, but i cannot seem to find/figure out how to actually implement that.

因此,当我单击按钮时,它立即将 label1 显示为“第二个”,而不是更改为“第一个”,等待 2 秒,然后更改为“第二个”。我在这里阅读了很多关于使用计时器而不是 thread.sleep 的线程,但我似乎无法找到/弄清楚如何实际实现它。

采纳答案by poke

timer.Start()just starts the timer but immediately returns while the timer is running in the background. So between setting the label text to firstand to secondthere is nearly no pause. What you want to do is wait for the timer to tick and only then update the label again:

timer.Start()只是启动计时器,但在计时器在后台运行时立即返回。因此,在将标签文本设置为first和之间second几乎没有停顿。您想要做的是等待计时器打勾,然后再次更新标签:

void timer_Tick(object sender, EventArgs e)
{
    timer.Stop();
    label1.Text = "second";
}

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    timer.Start();
}

Btw. you should not set timer.Enabledto true, you are already starting the timer using timer.Start().

顺便提一句。您不应该设置timer.Enabled为 true,您已经在使用timer.Start().

As mentioned in the comments, you could put the timer creation into a method, like this (note: this is untested):

如评论中所述,您可以将计时器创建放入一个方法中,如下所示(注意:这是未经测试的):

public void Delayed(int delay, Action action)
{
    Timer timer = new Timer();
    timer.Interval = delay;
    timer.Tick += (s, e) => {
        action();
        timer.Stop();
    };
    timer.Start();
}

And then you could just use it like this:

然后你可以像这样使用它:

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    Delayed(2000, () => label1.Text = "second");
}

Tergiver's follow-up

特吉弗的后续

Does using Delayed contain a memory leak (reference leak)?

Subscribing to an event always creates a two-way reference.

In this case timer.Tickgets a reference to an anonymous function (lambda). That function lifts a local variable timer, though it's a reference, not a value, and contains a reference to the passed in Action delegate. That delegate is going to contain a reference to label1, an instance member of the Form. So is there a circular reference from the Timerto the Form?

I don't know the answer, I'm finding it a bit difficult to reason about. Because I don't know, I would remove the use of the lambda in Delayed, making it a proper method and having it, in addition to stopping the timer (which is the senderparameter of the method), also remove the event.

使用 Delayed 是否包含内存泄漏(引用泄漏)?

订阅事件总是会创建双向引用。

在这种情况下,timer.Tick获取对匿名函数 (lambda) 的引用。该函数提升了一个局部变量timer,尽管它是一个引用,而不是一个值,并且包含对传入的 Action 委托的引用。该委托将包含对 的引用label1, 的实例成员Form。那么是否存在从 theTimer到 the的循环引用Form

我不知道答案,我发现这有点难以推理。因为我不知道,所以我会删除 lambda 中的使用Delayed,使其成为一个正确的方法,并拥有它,除了停止计时器(这是sender方法的参数)之外,还删除事件。

Usually lambdas do not cause problems for the garbage collection. In this case, the timer instance only exists locally and the reference in the lambda does not prevent the garbage collection to collect the instances (see also this question).

通常 lambda 不会导致垃圾收集问题。在这种情况下,计时器实例仅存在于本地,并且 lambda 中的引用不会阻止垃圾收集来收集实例(另请参阅此问题)。

I actually tested this again using the .NET Memory Profiler. The timer objects were collected just fine, and no leaking happened. The profiler did give me a warning that there are instances that “[…] have been garbage collected without being properly disposed” though. Removing the event handler in itself (by keeping a reference to it) did not fix that though. Changing the captured timer reference to (Timer)sdid not change that either.

我实际上使用 .NET Memory Profiler 再次对此进行了测试。定时器对象收集得很好,没有发生泄漏。分析器确实给了我一个警告,说有些情况“[...] 没有被正确处理就被垃圾收集了”。删除事件处理程序本身(通过保留对它的引用)并没有解决这个问题。将捕获的计时器引用更改为(Timer)s也没有改变。

What did help—obviously—was to call a timer.Dispose()in the event handler after stopping the timer, but I'd argue if that is actually necessary. I don't think the profiler warning/note is that critical.

有什么帮助——显然——是timer.Dispose()在停止计时器后在事件处理程序中调用 a ,但我认为这是否真的有必要。我不认为分析器警告/注释那么重要。

回答by Shane.C

If all you're trying to do is change the text when the timer ticks, would you not be better off putting...

如果您要做的只是在计时器滴答作响时更改文本,那么您最好将...

label1.Text = "second";

...In the timer tick, either before or after you change the timer to enabled = false;

...在计时器滴答中,无论是在您将计时器更改为 enabled = false 之前还是之后;

Like so;

像这样;

void timer_Tick(object sender, EventArgs e)
{
  timer.Stop();
  label1.Text = "second";
}

private void button1_Click(object sender, EventArgs e)
{
  label1.Text = "first";
  timer.Start();
}

回答by Servy

If you're using C# 5.0 awaitmakes this mucheasier:

如果您使用的是 C# 5.0 await,那么这更容易:

private async void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    await Task.Delay(2000);
    label1.Text = "second";
}

回答by Yuval

       private bool Delay(int millisecond)       
       {

           Stopwatch sw = new Stopwatch();
           sw.Start();
           bool flag = false;
           while (!flag)
           {
               if (sw.ElapsedMilliseconds > millisecond) 
               {
                  flag = true;
               }
           }
           sw.Stop();
           return true;

       }

        bool del = Delay(1000);