web-dev-qa-db-de.com

Start darf nicht für eine versprochene Aufgabe aufgerufen werden. Ausnahme kommt

Ich erstelle eine einfache Wpf-Desktop-Anwendung. Ich habe nur eine Schaltfläche und Code in .cs-Datei wie.

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

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

Aber überraschenderweise wirft Zeile Task.Delay(5000).Start(); ein InvalidOperationException:

Start darf nicht für eine versprochene Aufgabe aufgerufen werden.

Kann jemand helfen, warum es so ist?

102
D J

Sie erhalten diesen Fehler, weil die Klasse Task die Aufgabe bereits gestartet hat, bevor sie Ihnen übergeben wurde. Sie sollten Start immer nur für eine Aufgabe aufrufen, die Sie durch Aufrufen ihres Konstruktors erstellen, und Sie sollten dies nicht einmal tun, es sei denn, Sie haben einen zwingenden Grund, die Aufgabe beim Erstellen nicht zu starten. Wenn Sie möchten, dass es sofort startet, sollten Sie Task.Run oder Task.Factory.StartNew, um ein neues Task zu erstellen und zu starten.

Jetzt wissen wir also, dass wir diesen lästigen Start loswerden müssen. Sie werden Ihren Code ausführen und feststellen, dass das Meldungsfeld sofort und nicht 5 Sekunden später angezeigt wird. Was ist damit los?

Gut, Task.Delay gibt dir nur eine Aufgabe, die in 5 Sekunden erledigt ist. Die Ausführung des Threads wird nicht für 5 Sekunden gestoppt. Was Sie tun möchten, ist ein Code, der ausgeführt wird, nachdem diese Aufgabe abgeschlossen ist. Dafür ist ContinueWith. Hiermit können Sie Code ausführen, nachdem eine bestimmte Aufgabe erledigt ist:

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

Dies wird sich wie erwartet verhalten.

Wir könnten auch das Schlüsselwort await von C # 5.0 verwenden, um Fortsetzungen einfacher hinzuzufügen:

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

Eine vollständige Erklärung dessen, was hier vor sich geht, würde den Rahmen dieser Frage sprengen, doch das Endergebnis ist eine Methode, die sich der vorherigen Methode sehr ähnlich verhält. 5 Sekunden nach dem Aufrufen der Methode wird ein Meldungsfeld angezeigt, die Methode selbst kehrt jedoch in beiden Fällen [fast] sofort zurück. Das heißt, await ist sehr mächtig und ermöglicht es uns, Methoden zu schreiben, die einfach und unkompliziert erscheinen, aber mit ContinueWith direkt viel schwieriger und unübersichtlicher zu schreiben wären. Es vereinfacht auch den Umgang mit der Fehlerbehandlung erheblich und spart eine Menge Code.

157
Servy

Versuche dies.

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

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