Das Problem ist, dass Sie die nicht generische Task
Klasse verwenden, die kein Ergebnis erzeugen soll. Wenn Sie also die Task
Instanz erstellen, die einen asynchronen Delegaten übergibt:
Task myTask = new Task(async () =>
... der Delegierte wird behandelt als async void
. An async void
ist kein Task
, 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 generische Task<TResult>
Klasse zu verwenden, da Sie ein Ergebnis zurückgeben möchten und das Ergebnis ein anderes ist Task
. Sie müssen also Folgendes erstellen Task<Task>
:
Task<Task> myTask = new Task<Task>(async () =>
Jetzt, wenn Sie Start
das Äußere Task<Task>
, wird es fast sofort abgeschlossen sein, weil seine Aufgabe nur darin besteht, das Innere zu schaffen Task
. Sie müssen dann auch auf das Innere warten Task
. So kann es gemacht werden:
myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;
Sie haben zwei Alternativen. Wenn Sie keinen expliziten Verweis auf das Innere benötigen Task
, können Sie das Äußere nur Task<Task>
zweimal abwarten :
await await myTask;
... oder Sie können die integrierte Erweiterungsmethode verwenden Unwrap
, die die äußeren und inneren Aufgaben zu einer Aufgabe kombiniert:
await myTask.Unwrap();
Dieses Auspacken erfolgt automatisch, wenn Sie die viel beliebtere Task.Run
Methode verwenden, mit der heiße Aufgaben erstellt werden. Daher wird diese Methode Unwrap
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 die myTask
Variable als vom Typ deklarieren Task<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 ein Func<T>
Argument (dh zumindest das generierte Argument Task
wird nicht ignoriert) erwarten . In dem unglücklichen Fall, dass diese Methode eine akzeptiert Action
, wird Ihr Delegierter als behandelt async void
. Dies ist selten das, was Sie wollen, wenn überhaupt.