13

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

我将计时器设置为 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";
}

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

4

4 回答 4

12

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

private async void button1_Click(object sender, EventArgs e)
{
    label1.Text = "first";
    await Task.Delay(2000);
    label1.Text = "second";
}
于 2013-01-02T17:10:11.053 回答
11

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();
}

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

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

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

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

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

Tergiver的后续行动

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

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

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

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

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

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

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

于 2013-01-02T17:05:39.677 回答
0

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

label1.Text = "second";

...在计时器滴答声中,在您将计时器更改为启用之前或之后 = false;

像这样;

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();
}
于 2013-01-02T17:05:23.480 回答
0
       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);
于 2017-05-14T18:32:23.643 回答