Запуск не може бути задіяний у завданні стилю обіцянки. настає виняток


108

Я створюю простий настільний додаток 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

Як сказав Серві, завдання вже розпочалося, тому все, що вам залишилося зробити, це почекати його (. Зачекайте ()):

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

1
Виклик Wait()завдання блокує поточний потік, поки завдання не вирішиться. Це майже ніколи те, що ви хочете, щоб сталося.
Джеремі

1
@Jeremy: Дійсно, варто звернути увагу на поведінку, про яку ви згадали, але в цьому випадку його FunctionA вже блокував поточну нитку, тому я припустив, що він просто шукає спосіб визначити, коли завдання буде виконано. Щоб уточнити різницю між Wait та async (для майбутніх читачів), будь ласка, прочитайте це посилання
Sergiu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.