Ich habe diesen Unit-Test durchgeführt und verstehe nicht, warum das "Warten auf Task.Delay ()" nicht wartet!
[TestMethod]
public async Task SimpleTest()
{
bool isOK = false;
Task myTask = new Task(async () =>
{
Console.WriteLine("Task.BeforeDelay");
await Task.Delay(1000);
Console.WriteLine("Task.AfterDelay");
isOK = true;
Console.WriteLine("Task.Ended");
});
Console.WriteLine("Main.BeforeStart");
myTask.Start();
Console.WriteLine("Main.AfterStart");
await myTask;
Console.WriteLine("Main.AfterAwait");
Assert.IsTrue(isOK, "OK");
}
Hier ist die Unit Test-Ausgabe:
Wie ist das möglich, dass ein "Warten" nicht wartet und der Haupt-Thread fortgesetzt wird?
Task.Run()
nach dem ersten verwendenConsole.WriteLine
?Antworten:
new Task(async () =>
Eine Aufgabe braucht nicht ein
Func<Task>
, sondern einAction
. Es ruft Ihre asynchrone Methode auf und erwartet, dass sie bei der Rückkehr endet. Aber das tut es nicht. Es gibt eine Aufgabe zurück. Diese Aufgabe wird von der neuen Aufgabe nicht erwartet. Für die neue Aufgabe ist der Job erledigt, sobald die Methode zurückgegeben wurde.Sie müssen die bereits vorhandene Aufgabe verwenden, anstatt sie in eine neue Aufgabe einzuschließen:
quelle
Task.Run(async() => ... )
ist auch eine OptionmyTask.Start();
wird einInvalidOperationException
myTask.Start()
, eine Ausnahme für seine Alternative ergeben würde und bei der Verwendung entfernt werden sollteTask.Run(...)
. Ihre Lösung enthält keinen Fehler.Das Problem ist, dass Sie die nicht generische
Task
Klasse verwenden, die kein Ergebnis erzeugen soll. Wenn Sie also dieTask
Instanz erstellen, die einen asynchronen Delegaten übergibt:... der Delegierte wird behandelt als
async void
. Anasync void
ist keinTask
, es kann nicht erwartet werden, seine Ausnahme kann nicht behandelt werden, und es ist eine Quelle von Tausenden von Fragen, die von frustrierten Programmierern hier in StackOverflow und anderswo gestellt werden. Die Lösung besteht darin, die generischeTask<TResult>
Klasse zu verwenden, da Sie ein Ergebnis zurückgeben möchten und das Ergebnis ein anderes istTask
. Sie müssen also Folgendes erstellenTask<Task>
:Jetzt, wenn Sie
Start
das ÄußereTask<Task>
, wird es fast sofort abgeschlossen sein, weil seine Aufgabe nur darin besteht, das Innere zu schaffenTask
. Sie müssen dann auch auf das Innere wartenTask
. So kann es gemacht werden:Sie haben zwei Alternativen. Wenn Sie keinen expliziten Verweis auf das Innere benötigen
Task
, können Sie das Äußere nurTask<Task>
zweimal abwarten :... oder Sie können die integrierte Erweiterungsmethode verwenden
Unwrap
, die die äußeren und inneren Aufgaben zu einer Aufgabe kombiniert:Dieses Auspacken erfolgt automatisch, wenn Sie die viel beliebtere
Task.Run
Methode verwenden, mit der heiße Aufgaben erstellt werden. Daher wird diese MethodeUnwrap
heutzutage nicht mehr sehr häufig verwendet.Wenn Sie entscheiden, dass Ihr asynchroner Delegat ein Ergebnis zurückgeben muss, z. B. a
string
, sollten Sie diemyTask
Variable als vom Typ deklarierenTask<Task<string>>
.Hinweis: Ich unterstütze die Verwendung von
Task
Konstruktoren zum Erstellen kalter Aufgaben nicht. Da eine Praxis aus Gründen, die ich nicht wirklich kenne, im Allgemeinen verpönt ist, aber wahrscheinlich, weil sie so selten verwendet wird, dass sie andere ahnungslose Benutzer / Betreuer / Prüfer des Codes überraschen kann.Allgemeiner Rat: Seien Sie jedes Mal vorsichtig, wenn Sie einen asynchronen Delegaten als Argument für eine Methode angeben. Diese Methode sollte idealerweise ein
Func<Task>
Argument (dh asynchrone Delegaten) oder zumindest einFunc<T>
Argument (dh zumindest das generierte ArgumentTask
wird nicht ignoriert) erwarten . In dem unglücklichen Fall, dass diese Methode eine akzeptiertAction
, wird Ihr Delegierter als behandeltasync void
. Dies ist selten das, was Sie wollen, wenn überhaupt.quelle
quelle
await
Aufgabe diese Aufgabe nicht wirklich verzögern wird, oder? Sie haben die Funktionalität entfernt.