Старт не может быть вызван задачей с обещанием. исключение приходит

Я создаю простое настольное приложение для 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:

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

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

Ответ 1

Вы получаете эту ошибку, потому что класс 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");
    });
}

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

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

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

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

Ответ 2

Попробуйте это.

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

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

Ответ 3

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

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