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()
ist im Grunde das gleiche wie Thread.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 sollten await Task.WhenAll
stattdessen verwenden.
ConfigureAwait(false)
einen einzelnen Anruf zum Deadlock haben Bar
oder Ros
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 Sie await 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 das Task.WhenAll
in ein setzen AsyncPump.Run
, wird es das Ganze effektiv blockieren, ohne dass Sie ConfigureAwait
irgendwo hin müssen, aber das ist wahrscheinlich eine zu komplexe Lösung.
Task.Delay(1).Wait()
dem ist gut genug.