Как использовать таймер для ожидания?

Я пытаюсь задержать события в моем методе с помощью таймера, однако я не всегда понимаю, как использовать таймер для ожидания.

Я установил мой таймер на 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, но я не могу найти/выяснить, как это реализовать.

Ответ 1

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. вы не должны устанавливать timer.Enabled в true, вы уже timer.Start() таймер, используя 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");
}

Последующие действия

Использует ли Delayed утечку памяти (эталонная утечка)?

Подписка на событие всегда создает двухстороннюю ссылку.

В этом случае timer.Tick получает ссылку на анонимную функцию (лямбда). Эта функция поднимает локальный timer переменной, хотя это ссылка, а не значение, и содержит ссылку на переданный в делетете действий. Этот делегат будет содержать ссылку на label1, член экземпляра Form. Так есть ли циркулярная ссылка от Timer к Form?

Я не знаю ответа, мне это сложно понять. Поскольку я не знаю, я бы удалил использование лямбда в Delayed, сделав его правильным методом и имея его, в дополнение к остановке таймера (который является параметром sender метода), также удалите событие.

Обычно лямбды не создают проблем для сбора мусора. В этом случае экземпляр таймера существует только локально, а ссылка в лямбда не мешает сборку мусора собирать экземпляры (см. Также этот вопрос).

Я действительно проверил это снова, используя.NET Memory Profiler. Объекты таймера были собраны просто отлично, и утечки не произошло. Профилировщик дал мне предупреждение, что есть случаи, когда "[...] были собраны мусор, не будучи должным образом удалены". Однако удаление обработчика событий (путем ссылки на него) не исправить. Изменение снятой ссылки таймера на (Timer)s тоже не изменилось.

То, что помогло, очевидно, в том, чтобы вызвать timer.Dispose() в обработчике событий после остановки таймера, но Id аргументирует, действительно ли это необходимо. Я не думаю, что предупреждение/примечание профилировщика критично.

Ответ 2

Если вы используете С# 5.0 await делает это гораздо проще:

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

Ответ 3

Если все, что вы пытаетесь сделать, это изменить текст, когда таймер тикает, не лучше ли было бы...

label1.Text = "second";

... В тике таймера, до или после изменения таймера на enabled = 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();
}

Ответ 4

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