Quando utilizzare Task.Delay, quando utilizzare Thread.Sleep?


386

Ci sono buone regole per quando usare Task.Delay contro Thread.Sleep ?

  • In particolare, esiste un valore minimo per garantire che uno sia efficace / efficiente rispetto all'altro?
  • Infine, poiché Task.Delay provoca il cambio di contesto su una macchina a stati asincroni / wait, c'è un sovraccarico nell'usarlo?

2
10ms sono molti cicli nel mondo dei computer ...
Brad Christie,

Quanto dovrebbe essere veloce? Quali problemi di prestazioni hai?
LB

4
Penso che la domanda più pertinente sia in quale contesto intende utilizzare una di queste? Senza tali informazioni la portata è troppo ampia. Cosa intendi con efficace / efficiente? Ti riferisci a precisione, efficienza energetica ecc.? Sono molto curioso di sapere in quale contesto è importante.
James World,

4
Il minimo è 15.625 msec, i valori inferiori alla frequenza di interruzione del clock non hanno alcun effetto. Task.Delay brucia sempre un System.Threading.Timer, Sleep non ha costi generali. Non ti preoccupare del sovraccarico quando scrivi codice che non fa nulla.
Hans Passant,

qualcosa che non ho visto menzionato, ma penso che sia importante, sarebbe Task.Delay che supporta un CancelToken, il che significa che puoi interrompere il ritardo, se, ad esempio, lo stai usando per rallentare un processo del ciclo. questo significa anche che il processo può rispondere rapidamente quando si desidera annullarlo. ma puoi ottenere lo stesso risultato con Thread.Sleep riducendo l'intervallo del ciclo di sospensione e controlla la riproduzione manuale dei token.
Droa,

Risposte:


370

Utilizzare Thread.Sleepquando si desidera bloccare il thread corrente.

Utilizzare Task.Delayquando si desidera un ritardo logico senza bloccare il thread corrente.

L'efficienza non dovrebbe essere una preoccupazione fondamentale per questi metodi. Il loro principale utilizzo nel mondo reale è come i tentativi di ripetizione delle operazioni di I / O, che sono nell'ordine dei secondi anziché dei millisecondi.


3
È lo stesso caso d'uso principale: un timer di ripetizione.
Stephen Cleary,

4
O quando non vuoi masticare la CPU in un loop principale.
Eddie Parker,

5
@RoyiNamir: No. Non esiste un "altro thread". Internamente, è implementato con un timer.
Stephen Cleary,

20
Il suggerimento di non preoccuparsi dell'efficienza è sconsigliato. Thread.Sleepbloccherà il thread corrente che provoca un cambio di contesto. Se si utilizza un pool di thread, ciò potrebbe anche comportare l'allocazione di un nuovo thread. Entrambe le operazioni sono piuttosto pesanti, mentre il multi-tasking cooperativo fornito da Task.Delayetc è progettato per evitare tutte queste spese generali, massimizzare la produttività, consentire la cancellazione e fornire un codice più pulito.
Corillian,

2
@LucaCremry onesi: I would use Thread.Sleep` per attendere all'interno di un metodo sincrono. Tuttavia, non lo faccio mai nel codice di produzione; nella mia esperienza, ogni Thread.Sleepcosa che abbia mai visto è stata indicativa di un tipo di problema di progettazione che deve essere risolto correttamente.
Stephen Cleary,

243

La differenza più grande tra Task.Delaye Thread.Sleepè che Task.Delaysi intende eseguire in modo asincrono. Non ha senso utilizzare Task.Delaynel codice sincrono. È un'idea MOLTO negativa da utilizzare Thread.Sleepnel codice asincrono.

Normalmente chiamerai Task.Delay() con la awaitparola chiave:

await Task.Delay(5000);

oppure, se si desidera eseguire un codice prima del ritardo:

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

Indovina cosa verrà stampato? In esecuzione per 0,0070048 secondi. Se invece spostiamo quanto await delaysopra Console.WriteLine, verrà stampato In esecuzione per 5.0020168 secondi.

Diamo un'occhiata alla differenza con 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");
    }
}

Prova a prevedere cosa verrà stampato ...

asincrono: avvio
asincrono: in esecuzione per 0,0070048 secondi
sincronizzazione: avvio
asincrono: in esecuzione per 5,0119008 secondi
asincrono:
sincronizzazione completata: in esecuzione per
sincronizzazione 5,000068 secondi : Fine

Inoltre, è interessante notare che Thread.Sleepè molto più preciso, l'accuratezza ms non è davvero un problema, mentre Task.Delaypuò richiedere un minimo di 15-30ms. Il sovraccarico su entrambe le funzioni è minimo rispetto alla precisione in ms che hanno (usa StopwatchClass se hai bisogno di qualcosa di più accurato). Thread.Sleeplega ancora il tuo thread, Task.Delayrilascialo per fare altri lavori mentre aspetti.


15
Perché è "MOLTO cattiva idea usare Thread.Sleep in codice asincrono"?
lato sole

69
@sunside Uno dei principali vantaggi del codice asincrono è quello di consentire a un thread di lavorare su più attività contemporaneamente, evitando il blocco delle chiamate. Ciò evita la necessità di enormi quantità di singoli thread e consente a un pool di thread di soddisfare molte richieste contemporaneamente. Tuttavia, dato che il codice asincrono di solito viene eseguito sul threadpool, il blocco inutile di un singolo thread con Thread.Sleep()consuma un intero thread che potrebbe altrimenti essere utilizzato altrove. Se molte attività vengono eseguite con Thread.Sleep (), c'è un'alta probabilità di esaurire tutti i thread di thread pool e compromettere seriamente le prestazioni.
Ryan

1
Prendilo. Mi mancava la nozione di codice asincrono nel senso dei asyncmetodi in quanto sono incoraggiati ad essere utilizzati. Fondamentalmente è solo una cattiva idea eseguire Thread.Sleep()in un thread threadpool, non una cattiva idea in generale. Dopotutto, c'è TaskCreationOptions.LongRunningquando si percorre il percorso (anche se scoraggiato) Task.Factory.StartNew().
Sunside

6
complimenti perawait wait
Eric Wu,

2
@Reyhn La documentazione su questo è che Tasl.Delayutilizza il timer di sistema. Poiché "L'orologio di sistema" ticchetta "a una velocità costante", la velocità di ticchettio del timer di sistema è di circa 16 ms, qualsiasi ritardo richiesto verrà arrotondato a un numero di tick dell'orologio di sistema, compensato dal tempo fino al primo tick. Consulta la documentazione Task.Delay msdn su docs.microsoft.com/en-us/dotnet/api/… e scorri verso il basso fino alle osservazioni.
Dorus,

28

se il thread corrente viene ucciso e tu usi Thread.Sleeped è in esecuzione, potresti ottenere un ThreadAbortException. Con Task.Delayte puoi sempre fornire un token di annullamento e ucciderlo con garbo. Questo è uno dei motivi per cui sceglierei Task.Delay. vedi http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx

Concordo anche che l'efficienza non è fondamentale in questo caso.


2
Supponiamo che abbiamo ottenuto la seguente situazione: await Task.Delay(5000). Quando uccido il compito ottengo TaskCanceledException(e lo sopprimo) ma il mio thread è ancora vivo. ! Neat :)
AlexMelw,

24

Voglio aggiungere qualcosa In realtà, Task.Delayè un meccanismo di attesa basato sul timer. Se guardi la fonte troverai un riferimento a una Timerclasse che è responsabile del ritardo. D'altra parte, in Thread.Sleeprealtà fa dormire il thread corrente, in questo modo stai solo bloccando e sprecando un thread. Nel modello di programmazione asincrona si dovrebbe sempre usare Task.Delay()se si desidera che qualcosa (continuazione) accada dopo un certo ritardo.


'await Task.Delay ()' libera il thread per fare altre cose fino alla scadenza del timer, azzerato al 100%. Ma cosa succede se non riesco a usare 'wait' poiché il metodo non ha il prefisso 'async'? Quindi posso solo chiamare 'Task.Delay ()'. In tal caso il thread è ancora bloccato ma ho il vantaggio di annullare il Delay () . È corretto?
Erik Stroeken,

5
@ErikStroeken È possibile passare i token di annullamento sia al thread che all'attività. Task.Delay (). Wait () bloccherà, mentre Task.Delay () crea semplicemente l'attività se utilizzata senza attendere. Quello che fai con quell'attività dipende da te, ma il thread continua.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.