Start ne peut pas être appelé sur une tâche de type promesse. l'exception arrive

109

Je crée une application de bureau wpf simple. L'interface utilisateur n'a qu'un bouton et un code dans un fichier .cs comme.

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

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

Mais étonnamment, la ligne Task.Delay(5000).Start();jette un InvalidOperationException:

Start ne peut pas être appelé sur une tâche de type promesse.

Quelqu'un peut-il expliquer pourquoi c'est comme ça?

DJ
la source

Réponses:

171

Vous obtenez cette erreur parce que la Taskclasse a déjà commencé la tâche avant de vous la donner. Vous ne devriez jamais appeler Startune tâche que vous créez en appelant son constructeur, et vous ne devriez même pas le faire à moins d'avoir une raison impérieuse de ne pas démarrer la tâche lorsque vous la créez; si vous voulez qu'il démarre immédiatement, vous devez utiliser Task.Runou Task.Factory.StartNewpour créer et démarrer un nouveau fichier Task.

Donc, maintenant, nous savons qu'il faut simplement se débarrasser de cet embêtant Start. Vous exécuterez votre code et découvrirez que la boîte de message s'affiche tout de suite, pas 5 secondes plus tard, qu'est-ce qui se passe avec ça?

Eh bien, Task.Delayvous donne juste une tâche qui sera terminée en 5 secondes. Cela n'arrête pas l'exécution du thread pendant 5 secondes. Ce que vous voulez faire, c'est avoir du code qui est exécuté une fois cette tâche terminée. C'est à ça que ça ContinueWithsert. Il vous permet d'exécuter du code une fois qu'une tâche donnée est terminée:

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

Cela se comportera comme prévu.

Nous pourrions également tirer parti du awaitmot - clé de C # 5.0 pour ajouter des continuations plus facilement:

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

Bien qu'une explication complète de ce qui se passe ici dépasse le cadre de cette question, le résultat final est une méthode qui se comporte très similaire à la méthode précédente; il affichera une boîte de message 5 secondes après avoir appelé la méthode, mais la méthode elle-même retournera [presque] tout de suite dans les deux cas. Cela dit, awaitc'est très puissant et nous permet d'écrire des méthodes qui semblent simples et directes, mais ce serait beaucoup plus difficile et plus compliqué à écrire en utilisant ContinueWithdirectement. Cela simplifie également considérablement la gestion des erreurs, en supprimant beaucoup de code standard.

Servy
la source
1

Essaye ça.

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

public async void FunctionA()
{
    await Task.Delay(5000);
    MessageBox.Show("Waiting Complete");
}
Mathias Lykkegaard Lorenzen
la source
-4

Comme l'a dit Servy, la tâche a déjà commencé, il ne vous reste donc plus qu'à l'attendre (.Wait ()):

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}
public void FunctionA()
{
    Task.Delay(5000).Wait();
    MessageBox.Show("Waiting Complete");
}
Sergiu
la source
1
L'appel Wait()sur une tâche bloquera le thread actuel jusqu'à ce que la tâche soit résolue. Ce n'est presque jamais ce que vous voulez.
Jeremy
1
@Jeremy: En effet, cela vaut la peine de prêter attention au comportement que vous avez mentionné, mais dans ce cas, sa FunctionA bloquait déjà le thread actuel, donc j'ai supposé qu'il cherchait juste un moyen de déterminer quand la tâche est terminée. Pour clarifier la différence entre Wait et async (pour les futurs lecteurs), veuillez lire ce lien
Sergiu