Start kann möglicherweise nicht für eine Aufgabe im Versprechungsstil aufgerufen werden. Ausnahme kommt

107

Ich erstelle eine einfache wpf-Desktop-Anwendung. UI haben 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 Task.Delay(5000).Start();wirft die Linie ein InvalidOperationException:

Start kann möglicherweise nicht für eine Aufgabe im Versprechungsstil aufgerufen werden.

Kann jemand helfen, warum es so ist?

DJ
quelle

Antworten:

170

Sie erhalten diesen Fehler, weil die TaskKlasse die Aufgabe bereits gestartet hat, bevor sie sie Ihnen gegeben hat. Sie sollten Starteine von Ihnen erstellte Aufgabe immer nur durch Aufrufen ihres Konstruktors aufrufen, 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 gestartet wird, sollten Sie Task.Runoder verwenden Task.Factory.StartNew, um ein neues zu erstellen und zu starten Task.

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

Nun, Task.Delaygibt Ihnen nur eine Aufgabe, die in 5 Sekunden erledigt sein wird. Die Ausführung des Threads wird 5 Sekunden lang nicht gestoppt. Was Sie tun möchten, ist Code, der nach Abschluss dieser Aufgabe ausgeführt wird. Dafür ContinueWithist da. Sie können Code ausführen, nachdem eine bestimmte Aufgabe erledigt wurde:

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

Dies wird sich wie erwartet verhalten.

Wir könnten auch das awaitSchlüsselwort von C # 5.0 nutzen , um Fortsetzungen einfacher hinzuzufügen:

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

Während eine vollständige Erklärung der Vorgänge hier den Rahmen dieser Frage sprengt, ist das Endergebnis eine Methode, die sich der vorherigen Methode sehr ähnlich verhält. 5 Sekunden nach dem Aufruf der Methode wird ein Meldungsfeld angezeigt, aber die Methode selbst wird in beiden Fällen [fast] sofort zurückgegeben. Das heißt, es awaitist sehr mächtig und ermöglicht es uns, Methoden zu schreiben, die einfach und unkompliziert erscheinen, aber es wäre viel schwieriger und unordentlicher, ContinueWithdirekt damit zu schreiben . Es vereinfacht auch den Umgang mit der Fehlerbehandlung erheblich, da viel Code auf dem Boilerplate entfernt wird.

Servieren
quelle
1

Versuche dies.

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
quelle
-4

Wie Servy sagte, hat die Aufgabe bereits begonnen. Sie müssen also nur noch darauf warten (.Wait ()):

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}
public void FunctionA()
{
    Task.Delay(5000).Wait();
    MessageBox.Show("Waiting Complete");
}
Sergiu
quelle
1
Durch das Aufrufen Wait()einer Aufgabe wird der aktuelle Thread blockiert, bis die Aufgabe aufgelöst wird. Das ist fast nie das, was Sie wollen.
Jeremy
1
@Jeremy: In der Tat lohnt es sich, auf das von Ihnen erwähnte Verhalten zu achten, aber in diesem Fall blockierte seine FunctionA bereits den aktuellen Thread, daher nahm ich an, dass er nur nach einer Möglichkeit sucht, um festzustellen, wann die Aufgabe abgeschlossen ist. Um den Unterschied zwischen Warten und Asynchronisieren (für zukünftige Leser) zu verdeutlichen, lesen Sie bitte diesen Link
Sergiu