Wann wird Task.Delay verwendet, wann wird Thread.Sleep verwendet?

385

Gibt es gute Regeln für die Verwendung von Task.Delay im Vergleich zu Thread.Sleep ?

  • Gibt es einen Mindestwert, um sicherzustellen, dass einer gegenüber dem anderen effektiv / effizient ist?
  • Gibt es einen Overhead bei der Verwendung von Task.Delay, da die Kontextumschaltung auf einer asynchronen / wartenden Zustandsmaschine verursacht wird?
Tom K.
quelle
2
10ms ist eine Menge Zyklen in der Computerwelt ...
Brad Christie
Wie schnell soll es sein? Welche Leistungsprobleme haben Sie?
LB
4
Ich denke, die relevantere Frage ist, in welchem ​​Kontext Sie beide verwenden möchten. Ohne diese Informationen ist der Anwendungsbereich zu breit. Was meinst du mit effektiv / effizient? Beziehen Sie sich auf Genauigkeit, Energieeffizienz usw.? Ich bin sehr gespannt, in welchem ​​Kontext dies wichtig ist.
James World
4
Das Minimum beträgt 15,625 ms, Werte unter der Taktinterruptrate haben keine Auswirkung. Task.Delay verbrennt immer ein System.Threading.Timer, Sleep hat keinen Overhead. Sie machen sich keine Sorgen über den Overhead, wenn Sie Code schreiben, der nichts bewirkt.
Hans Passant
etwas, das ich nicht erwähnt gesehen habe, aber ich denke, es ist wichtig, dass Task.Delay ein CancellationToken unterstützt, was bedeutet, dass Sie die Verzögerung unterbrechen können, wenn Sie es beispielsweise verwenden, um einen Zyklusprozess zu verlangsamen. Dies bedeutet auch, dass Ihr Prozess schnell reagieren kann, wenn Sie ihn abbrechen möchten. Sie können dies jedoch auch mit Thread.Sleep erreichen, indem Sie das Schlafzyklusintervall verkürzen und das Token-Handbuch überprüfen.
Droa

Antworten:

369

Verwenden Thread.SleepSie diese Option, wenn Sie den aktuellen Thread blockieren möchten.

Verwenden Task.DelaySie diese Option, wenn Sie eine logische Verzögerung wünschen, ohne den aktuellen Thread zu blockieren.

Effizienz sollte bei diesen Methoden kein vorrangiges Anliegen sein. Sie werden hauptsächlich in der Praxis als Wiederholungszeitgeber für E / A-Vorgänge verwendet, die eher in der Größenordnung von Sekunden als von Millisekunden liegen.

Stephen Cleary
quelle
3
Es ist der gleiche primäre Anwendungsfall: ein Wiederholungs-Timer.
Stephen Cleary
4
Oder wenn Sie die CPU nicht in einer Hauptschleife zerkauen möchten.
Eddie Parker
5
@ RoyiNamir: Nein. Es gibt keinen "anderen Thread". Intern wird es mit einem Timer implementiert.
Stephen Cleary
20
Der Vorschlag, sich keine Sorgen um die Effizienz zu machen, ist nicht ratsam. Thread.Sleepblockiert den aktuellen Thread, der einen Kontextwechsel verursacht. Wenn Sie einen Thread-Pool verwenden, kann dies auch dazu führen, dass ein neuer Thread zugewiesen wird. Beide Vorgänge sind ziemlich umfangreich, während das von Task.Delayetc bereitgestellte kooperative Multitasking den gesamten Overhead vermeiden, den Durchsatz maximieren, die Stornierung ermöglichen und saubereren Code bereitstellen soll.
Corillian
2
@LucaCremry onesi: I would use Thread.Sleep`, um innerhalb einer synchronen Methode zu warten. Im Produktionscode mache ich das jedoch nie. Nach meiner Erfahrung war alles, was Thread.Sleepich jemals gesehen habe, ein Hinweis auf ein Designproblem, das ordnungsgemäß behoben werden muss.
Stephen Cleary
243

Der größte Unterschied zwischen Task.Delayund Thread.Sleepbesteht darin, dass Task.Delayasynchron ausgeführt werden soll. Die Verwendung Task.Delayin synchronem Code ist nicht sinnvoll . Es ist eine sehr schlechte Idee, Thread.Sleepin asynchronem Code zu verwenden.

Normalerweise rufen Sie Task.Delay() mit dem awaitSchlüsselwort an:

await Task.Delay(5000);

oder, wenn Sie vor der Verzögerung Code ausführen möchten:

var sw = new Stopwatch();
sw.Start();
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;

Ratet mal, was dies drucken wird? Läuft für 0,0070048 Sekunden. Wenn wir stattdessen das await delayObige verschieben Console.WriteLine, wird Running für 5.0020168 Sekunden gedruckt.

Schauen wir uns den Unterschied an mit Thread.Sleep:

class Program
{
    static void Main(string[] args)
    {
        Task delay = asyncTask();
        syncCode();
        delay.Wait();
        Console.ReadLine();
    }

    static async Task asyncTask()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("async: Starting");
        Task delay = Task.Delay(5000);
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        await delay;
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("async: Done");
    }

    static void syncCode()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("sync: Starting");
        Thread.Sleep(5000);
        Console.WriteLine("sync: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("sync: Done");
    }
}

Versuchen Sie vorherzusagen, was dies drucken wird ...

async:
Async starten: 0,0070048 Sekunden laufen lassen
Synchronisation: Async starten :
5,0119008 Sekunden laufen lassen
async:
Synchronisierung abgeschlossen: 5,0020168 Sekunden lang laufen
Synchronisation: Fertig

Es ist auch interessant festzustellen , dass die Thread.SleepGenauigkeit weitaus genauer ist, die Genauigkeit von ms kein wirkliches Problem darstellt und Task.Delayminimal 15 bis 30 ms dauern kann. Der Overhead für beide Funktionen ist im Vergleich zu ihrer ms-Genauigkeit minimal (verwenden Sie StopwatchClass, wenn Sie etwas genaueres benötigen). Thread.SleepBindet Ihren Thread immer noch zusammen und gibt Task.Delayihn frei, um andere Arbeiten auszuführen, während Sie warten.

Dorus
quelle
15
Warum ist es "eine SEHR schlechte Idee, Thread.Sleep in asynchronem Code zu verwenden"?
Sunside
69
@sunside Einer der Hauptvorteile von asynchronem Code besteht darin, dass ein Thread mehrere Aufgaben gleichzeitig bearbeiten kann, indem das Blockieren von Anrufen vermieden wird. Dies vermeidet die Notwendigkeit großer Mengen einzelner Threads und ermöglicht es einem Threadpool, viele Anforderungen gleichzeitig zu bearbeiten. Angesichts der Tatsache, dass asynchroner Code normalerweise im Threadpool ausgeführt wird, Thread.Sleep()verbraucht das Blockieren eines einzelnen Threads unnötigerweise einen gesamten Thread, der andernfalls anderweitig verwendet werden könnte. Wenn viele Aufgaben mit Thread.Sleep () ausgeführt werden, besteht eine hohe Wahrscheinlichkeit, dass alle Threadpool-Threads erschöpft sind und die Leistung ernsthaft beeinträchtigt wird.
Ryan
1
Kapiert. Mir fehlte der Begriff des asynchronen Codes im Sinne von asyncMethoden, da deren Verwendung empfohlen wird. Es ist im Grunde nur eine schlechte Idee, Thread.Sleep()in einem Threadpool-Thread zu laufen, im Allgemeinen keine schlechte Idee. Immerhin gibt es, TaskCreationOptions.LongRunningwenn man den (wenn auch entmutigten) Task.Factory.StartNew()Weg geht.
Sunside
6
Ein großes Lob fürawait wait
Eric Wu
2
@Reyhn Die Dokumentation dazu Tasl.Delayverwendet den System-Timer. Da "Die Systemuhr" "mit einer konstanten Rate" tickt, beträgt die Tick-Geschwindigkeit des System-Timers etwa 16 ms. Jede von Ihnen angeforderte Verzögerung wird auf eine Anzahl von Ticks der Systemuhr gerundet, die um die Zeit bis zur ersten versetzt sind Tick. Task.Delay Lesen Sie die msdn-Dokumentation unter docs.microsoft.com/en-us/dotnet/api/… und scrollen Sie nach unten zu den Anmerkungen.
Dorus
28

Wenn der aktuelle Thread beendet wird und Sie ihn verwenden Thread.Sleepund ausführen, erhalten Sie möglicherweise einen ThreadAbortException. Mit können Task.DelaySie jederzeit ein Stornierungszeichen bereitstellen und es ordnungsgemäß töten. Das ist ein Grund, warum ich wählen würde Task.Delay. Siehe http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx

Ich stimme auch zu, dass Effizienz in diesem Fall nicht an erster Stelle steht.

Balanikas
quelle
2
Angenommen, wir haben die folgende Situation : await Task.Delay(5000). Wenn ich die Aufgabe töte, bekomme ich sie TaskCanceledException(und unterdrücke sie), aber mein Thread lebt noch. Ordentlich! :)
AlexMelw
24

Ich möchte etwas hinzufügen. Eigentlich Task.Delayist ein Timer-basierter Wartemechanismus. Wenn Sie sich die Quelle ansehen, finden Sie einen Verweis auf eine TimerKlasse, die für die Verzögerung verantwortlich ist. Auf der anderen Seite Thread.Sleepwird der aktuelle Thread tatsächlich in den Ruhezustand versetzt. Auf diese Weise blockieren und verschwenden Sie nur einen Thread. Im asynchronen Programmiermodell sollten Sie immer verwenden, Task.Delay()wenn nach einer gewissen Verzögerung etwas (Fortsetzung) passieren soll.

verschlüsselt
quelle
'warte auf Task.Delay ()' gibt den Thread frei, um andere Dinge zu tun, bis der Timer abläuft, 100% klar. Aber was ist, wenn ich 'await' nicht verwenden kann, da der Methode nicht 'async' vorangestellt ist? Dann kann ich nur 'Task.Delay ()' aufrufen. In diesem Fall ist der Thread immer noch blockiert, aber ich habe den Vorteil, Delay () abzubrechen . Ist das korrekt?
Erik Stroeken
5
@ErikStroeken Sie können Stornierungs-Token sowohl an den Thread als auch an die Aufgabe übergeben. Task.Delay (). Wait () wird blockiert, während Task.Delay () die Task nur erstellt, wenn sie ohne Wartezeit verwendet wird. Was Sie mit dieser Aufgabe machen, liegt bei Ihnen, aber der Thread geht weiter.