Запуск не может быть вызван для задачи в стиле обещания. приближается исключение


109

Я создаю простое настольное приложение wpf. Пользовательский интерфейс имеет только кнопку и код в файле .cs, например.

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}

public void FunctionA()
{
    Task.Delay(5000).Start();
    MessageBox.Show("Waiting Complete");
}

Но на удивление строчка Task.Delay(5000).Start();бросает InvalidOperationException:

Запуск не может быть вызван для задачи в стиле обещания.

Кто-нибудь может помочь, почему это так?

Ответы:


171

Вы получаете эту ошибку, потому что Taskкласс уже запустил задачу, прежде чем передать ее вам. Вы должны только когда-либо вызывать Startзадачу, которую вы создаете, вызывая ее конструктор, и вам даже не следует этого делать, если у вас нет веских причин не запускать задачу при ее создании; если вы хотите, чтобы это началось сразу, вы должны использовать Task.Runили Task.Factory.StartNewкак для создания, так и для начала нового Task.

Итак, теперь мы знаем, как просто избавиться от этого надоедливого Start. Вы запустите свой код и обнаружите, что окно сообщения отображается сразу, а не через 5 секунд, что с этим?

Ну Task.Delayпросто дает вам задание, которое будет выполнено за 5 секунд. Он не останавливает выполнение потока на 5 секунд. Что вы хотите сделать, так это иметь некоторый код, который будет выполняться после завершения этой задачи. Вот для чего ContinueWith. Он позволяет запускать некоторый код после выполнения данной задачи:

public void FunctionA()
{
    Task.Delay(5000)
    .ContinueWith(t => 
    {
        MessageBox.Show("Waiting Complete");
    });
}

Это будет вести себя так, как ожидалось.

Мы также могли бы использовать awaitключевое слово C # 5.0, чтобы упростить добавление продолжений:

public async Task FunctionA()
{
    await Task.Delay(5000);
    MessageBox.Show("Waiting Complete");
}

Хотя полное объяснение того, что здесь происходит, выходит за рамки этого вопроса, конечным результатом является метод, который ведет себя очень похоже на предыдущий метод; он покажет окно сообщения через 5 секунд после вызова метода, но сам метод вернет [почти] сразу же в обоих случаях. Тем не менее, awaitэто очень мощный инструмент, который позволяет нам писать методы, которые кажутся простыми и понятными, но которые было бы намного сложнее и сложнее писать, используя ContinueWithнапрямую. Это также значительно упрощает обработку ошибок, избавляясь от большого количества шаблонного кода.



-4

Как сказал Серви, задача уже началась, поэтому все, что вам осталось сделать, это дождаться ее (.Wait ()):

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}
public void FunctionA()
{
    Task.Delay(5000).Wait();
    MessageBox.Show("Waiting Complete");
}

1
Вызов Wait()задачи заблокирует текущий поток до тех пор, пока задача не будет решена. Такого почти никогда не бывает.
— Джереми

1
@ Джереми: Действительно, стоит обратить внимание на поведение, которое вы упомянули, но в этом случае его FunctionA уже блокирует текущий поток, поэтому я предположил, что он просто ищет способ определить, когда задача завершена. Чтобы прояснить разницу между Wait и async (для будущих читателей), прочтите эту ссылку
— Серджиу
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.