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

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

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

Ответ 1

(транскрибируется от Бена как комментарий)

просто используйте System.Windows.Forms.Timer. Установите таймер на 5 секунд и обработайте событие Tick. Когда событие срабатывает, сделайте это.

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

Событие Tick может быть выполнено в другом потоке, который не может изменить ваш 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 и wait" в MSDN.

Ответ 2

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

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]

Ответ 4

ваше приложение зависает, потому что вы вызываете 5 секунд ожидания/ожидания в основном потоке пользовательского интерфейса. поместите действие sleep/wait/any в отдельный поток (на самом деле System.Windows.Forms.Timer должен сделать это для вас), и когда он завершит вызов, действие, которое превращает некоторый зеленый элемент управления. не забудьте проверить InvokeRequired. здесь короткий образец (SetText может быть вызван из другого потока, если вместо этого будет вызван вызов в основном потоке пользовательского интерфейса, где находится текстовое поле):

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

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

Ответ 5

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

Итак, что бы вы не хотели.

Пока идет x секунд.?

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

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

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

Ответ 6

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

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

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

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

Также см. fooobar.com/questions/285032/...

Ответ 7

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