Как асинхронно ждать X секунд и выполнить что-то?

Я знаю, что есть Thread.Sleep и System.Windows.Forms.Timer и Monitor.Wait в C# и Windows Forms. Я просто не могу понять, как ждать X секунд, а затем делать что - то еще-без блокировки потока.

у меня есть форма с кнопкой. При нажатии кнопки таймер запускается и ждет 5 секунд. После этих 5 секунд какой-то другой элемент управления на форме окрашивается в зеленый цвет. При использовании Thread.Sleep, все приложение станет невосприимчивым для 5 секунды-так как же мне просто "сделать что-то после 5 секунд"?

7 ответов


(расшифровано от Бена как комментарий)

просто используйте систему.Окна.Формы.Таймер. Установите таймер на 5 секунд и обработайте событие Tick. Когда событие вспыхнет, сделайте это.

...и отключите таймер (IsEnabled=false), прежде чем выполнять свою работу в oder, чтобы подавить секунду.

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

private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();

    private void StartAsyncTimedWork()
    {
        myTimer.Interval = 5000;
        myTimer.Tick += new EventHandler(myTimer_Tick);
        myTimer.Start();
    }

    private void myTimer_Tick(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            /* Not on UI thread, reenter there... */
            this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
        }
        else
        {
            lock (myTimer)
            {
                /* only work when this is no reentry while we are already working */
                if (this.myTimer.Enabled)
                {
                    this.myTimer.Stop();
                    this.doMyDelayedWork();
                    this.myTimer.Start(); /* optionally restart for periodic work */
                }
            }
        }
    }

просто для полноты: с async / await можно отложить выполнение чего-то очень простого (один выстрел, никогда не повторяйте вызов):

private async Task delayedWork()
{
    await Task.Delay(5000);
    this.doMyDelayedWork();
}

//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
    Task ignoredAwaitableResult = this.delayedWork();
}

дополнительные сведения см. В разделе "async and await" в MSDN.


Вы можете запустить асинхронную задачу, которая выполняет свои действия:

Task.Factory.StartNew(()=>
{
    Thread.Sleep(5000);
    form.Invoke(new Action(()=>DoSomething()));
});

[EDIT]

чтобы пройти интервал, вам просто нужно сохранить его в переменной:

int interval = 5000;
Task.Factory.StartNew(()=>
{
    Thread.Sleep(interval);
    form.Invoke(new Action(()=>DoSomething()));
});

[/EDIT]


вы смотрите на это неправильно. Нажмите кнопку, она запускает таймер с интервалом в x секунд. Когда они вверх, это eventhandler выполняет задачу.

Так что вы не хотите, чтобы произошло.

в то время как X секунд истекают.?

во время выполнения задачи?

Если, например, вы не хотите, чтобы кнопка была нажата до тех пор, пока задержка и задача не будут выполнены. Отключите его в обработчике нажатия кнопки и включите его в задаче завершение.

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


ваше приложение зависает, потому что вы вызываете 5-секундный сон/ожидание в основном потоке пользовательского интерфейса. поместите действие sleep/wait / whatever в отдельный поток (на самом деле система.Окна.Формы.Таймер должен сделать это за вас), и когда он завершится, вызовите действие, которое превратит некоторый контроль в зеленый. не забудьте проверить InvokeRequired. вот короткий пример (SetText может быть вызван из другого потока, если это вызов, вместо этого будет вызван в основном потоке пользовательского интерфейса, где находится текстовое поле on):

private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{    
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
}
else
{
    this.textBox1.Text = text;
}
}

Я взял образец от здесь (стоит прочитать!).


@eFloh в сообщении, помеченном как ответ, сказал:

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

Это не то, что говорят врачи.
Вы используете систему.Окна.Формы.Таймер в примере кода.
Это формы.Таймер.
Согласно документам C# события таймера вызываются в потоке пользовательского интерфейса.

Этот Windows таймер предназначен для однопоточной среде, где Потоки пользовательского интерфейса используются для выполнения обработки. Это требует, чтобы пользователь код имеет доступный насос сообщений UI и всегда работает от одного и того же нитка...

см. Также сообщение stackoverflow здесь


Я думаю, что вы ищете System.Timers ... Я не думаю, что вам нужно установить таймер CWND для того, что вы пытаетесь сделать: посмотрите наhttp://msdn.microsoft.com/en-us/library/0tcs6ww8 (v=VS.90).aspx Для примера. Это должно сработать.


ты пробовал

public static Task Delay(
    int millisecondsDelay
)

Вы можете использовать такой:

await Task.Deplay(5000);

ссылка:https://msdn.microsoft.com/en-us/library/hh194873 (v=против 110).aspx