Как асинхронно подождать х секунд и выполнить что-то потом? - PullRequest
17 голосов
/ 02 февраля 2012

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

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

Ответы [ 7 ]

29 голосов
/ 02 февраля 2012

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

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

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

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

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 можно отложить выполнение оченьeasy (один выстрел, никогда не повторять вызов):

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

Подробнее см. «Асинхронизация и ожидание» в MSDN.

10 голосов
/ 02 февраля 2012

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

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]

5 голосов
/ 21 июня 2018

Вы пробовали

public static Task Delay(
    int millisecondsDelay
)

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

await Task.Delay(5000);

ссылка: https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx

1 голос
/ 07 апреля 2013

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

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

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

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

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

1 голос
/ 02 февраля 2012

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

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

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

Пока задачаexecuting?

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

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

1 голос
/ 02 февраля 2012

ваше приложение зависает, потому что вы вызываете 5-секундный режим ожидания / ожидания в основном потоке пользовательского интерфейса. поместите действие сна / ожидания / что-либо в отдельный поток (на самом деле это должно сделать для вас 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;
}
}

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

0 голосов
/ 02 февраля 2012

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

...