Ich hatte eine längere Diskussion mit einem Kollegen darüber und er hat mir bewiesen, dass es signifikante Unterschiede gibt, die über das hinausgehen, was die beste Antwort derzeit zeigt. Wenn du await Task.Delay(EinigeMillisekunden)
verwendest, kannst du tatsächlich Aufrufer freigeben, die nicht direkt über dir im Stack stehen:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Gestartet " + Thread.CurrentThread.ManagedThreadId);
DoSomething1();
Console.WriteLine("Beendet " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(6000);
}
static async void DoSomething1()
{
Console.WriteLine("DoSomething1 Gestartet " + Thread.CurrentThread.ManagedThreadId);
var result = await DoSomething2();
Console.WriteLine("DoSomething1 Beendet " + Thread.CurrentThread.ManagedThreadId);
}
static async Task DoSomething2()
{
Console.WriteLine("DoSomething2 Gestartet " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(5000); // Wird DoSomething1 blockieren, aber Main freigeben
//Thread.Sleep(5000); // Wird alles blockieren, einschließlich Main
//await Task.FromResult(5); // Wird sofort zurückgegeben (nur zum Vergleich)
//await Task.Delay(0); // Was wird es tun, kannst du es erraten?
Console.WriteLine("DoSomething2 Beendet " + Thread.CurrentThread.ManagedThreadId);
return 0;
}
}
}
Spielen Sie mit diesem Code herum und beobachten Sie die unterschiedlichen Effekte der Verwendung von Delay
oder Sleep
. Die Erklärung geht über den Umfang dieser Antwort hinaus, kann aber zusammengefasst werden als "Async-Funktionen starten keinen neuen Thread, bis sie auf etwas warten, das nicht sofort ausgeführt werden kann (oder das Ergebnis bestimmt)". Dies ist die Ausgabe:
Gestartet 1
DoSomething1 Gestartet 1
DoSomething2 Gestartet 1
Beendet 1
DoSomething2 Beendet 4
DoSomething1 Beendet 4
Es geht nicht darum, dass DoSomething1();
in Main
"fire and forget" ist. Das kannst du beweisen, indem du den Sleep
verwendest. Beachte auch, dass wenn DoSomething2 aus Task.Delay "zurückkehrt", es auf einem anderen Thread läuft.
Diese Funktionen sind viel schlauer als ich dachte, und ich habe geglaubt, dass await
einfach einen neuen Thread startet, um Dinge zu erledigen. Ich gebe immer noch nicht vor, alles zu verstehen, aber das gegen-intuitive Ergebnis oben zeigt, dass viel mehr unter der Oberfläche passiert, als nur Threads zu starten, um Code auszuführen.