Ich verstehe den Unterschied zwischen Task.Wait
und nicht ganz await
.
Ich habe etwas Ähnliches wie die folgenden Funktionen in einem ASP.NET-WebAPI-Dienst:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Wo Get
wird Deadlock.
Was könnte das verursachen? Warum verursacht dies kein Problem, wenn ich eine blockierende Wartezeit anstelle von verwende await Task.Delay
?
Task.Delay(1).Wait()
dem ist gut genug.Task.Delay(1).Wait()
ist im Grunde das gleiche wieThread.Sleep(1000)
. Im tatsächlichen Produktionscode ist dies selten angemessen.WaitAll
verursachst den Deadlock. Weitere Informationen finden Sie unter dem Link zu meinem Blog in meiner Antwort. Sie solltenawait Task.WhenAll
stattdessen verwenden.ConfigureAwait(false)
einen einzelnen Anruf zum Deadlock habenBar
oderRos
nicht blockieren werden, aber weil Sie eine Aufzählung haben, die mehr als einen erstellt und dann auf all diese wartet, blockiert der erste Balken den zweiten. Wenn Sieawait Task.WhenAll
nicht auf alle Aufgaben warten, um den ASP-Kontext nicht zu blockieren, wird die Methode normal zurückgegeben..ConfigureAwait(false)
gesamten Baum bis zum Blockieren hinzuzufügen. Auf diese Weise versucht nie etwas, zum Hauptkontext zurückzukehren. das würde funktionieren. Eine andere Möglichkeit wäre, einen inneren Synchronisationskontext aufzurufen. Link . Wenn Sie dasTask.WhenAll
in ein setzenAsyncPump.Run
, wird es das Ganze effektiv blockieren, ohne dass SieConfigureAwait
irgendwo hin müssen, aber das ist wahrscheinlich eine zu komplexe Lösung.Antworten:
Wait
undawait
- obwohl konzeptionell ähnlich - tatsächlich völlig unterschiedlich sind.Wait
wird synchron blockiert, bis die Aufgabe abgeschlossen ist. Der aktuelle Thread wird also buchstäblich blockiert, bis die Aufgabe abgeschlossen ist. In der Regel sollten Sie "async
ganz nach unten" verwenden. Blockieren Sie also keinenasync
Code. In meinem Blog gehe ich auf die Details ein, wie das Blockieren von asynchronem Code zu einem Deadlock führt .await
wartet asynchron, bis die Aufgabe abgeschlossen ist. Dies bedeutet, dass die aktuelle Methode "angehalten" ist (ihr Status wird erfasst) und die Methode eine unvollständige Aufgabe an ihren Aufrufer zurückgibt. Später, wenn derawait
Ausdruck abgeschlossen ist, wird der Rest der Methode als Fortsetzung geplant.Sie haben auch einen "kooperativen Block" erwähnt, mit dem Sie vermutlich eine Aufgabe meinen, die Sie
Wait
im wartenden Thread ausführen können. Es gibt Situationen, in denen dies passieren kann, aber es ist eine Optimierung. Es gibt viele Situationen, in denen dies nicht möglich ist, z. B. wenn die Aufgabe für einen anderen Scheduler bestimmt ist oder wenn sie bereits gestartet wurde oder wenn es sich um eine Nicht-Code-Aufgabe handelt (wie in Ihrem Codebeispiel:Wait
DieDelay
Aufgabe kann nicht inline ausgeführt werden, da kein Code vorhanden ist dafür).Sie können mein
async
/await
Intro hilfreich finden.quelle
Wait
funktioniert gutawait
Deadlocks.Wait
blockiert den Thread und kann nicht für andere Zwecke verwendet werden.await
Code gearbeitet. Entweder das, oder der Deadlock hatte nichts damit zu tun, und Sie haben das Problem falsch diagnostiziert.SynchronizationContext
Blockieren innerhalb einer ASP.NET Core-Anforderung nicht mehr blockiert wurde.Basierend auf dem, was ich aus verschiedenen Quellen gelesen habe:
Ein
await
Ausdruck blockiert nicht den Thread, auf dem er ausgeführt wird. Stattdessen veranlasst es den Compiler, den Rest derasync
Methode als Fortsetzung der erwarteten Aufgabe anzumelden. Die Steuerung kehrt dann zum Aufrufer derasync
Methode zurück. Wenn die Aufgabe abgeschlossen ist, ruft sie ihre Fortsetzung auf und die Ausführung derasync
Methode wird dort fortgesetzt, wo sie aufgehört hat.Um auf den Abschluss eines einzelnen
task
zu warten , können Sie dessenTask.Wait
Methode aufrufen . Ein Aufruf derWait
Methode blockiert den aufrufenden Thread, bis die Ausführung der einzelnen Klasseninstanz abgeschlossen ist. Die parameterloseWait()
Methode wird verwendet, um bedingungslos zu warten, bis eine Aufgabe abgeschlossen ist. Die Aufgabe simuliert die Arbeit, indem dieThread.Sleep
Methode zwei Sekunden lang in den Ruhezustand versetzt wird.Dieser Artikel ist auch eine gute Lektüre.
quelle
Einige wichtige Fakten wurden in anderen Antworten nicht angegeben:
"async await" ist auf CIL-Ebene komplexer und kostet daher Speicher und CPU-Zeit.
Jede Aufgabe kann abgebrochen werden, wenn die Wartezeit nicht akzeptabel ist.
Für den Fall "async await" haben wir keinen Handler für eine solche Aufgabe, um sie abzubrechen oder zu überwachen.
Die Verwendung von Task ist flexibler als "asynchrones Warten".
Jede Synchronisierungsfunktion kann asynchron umbrochen werden.
Ich verstehe nicht, warum ich mit der Codeduplizierung für die Synchronisierungs- und Asynchronisierungsmethode oder mit Hacks leben muss.
quelle