Perché Thread.Sleep è così dannoso

Spesso vedo che menziona Thread.Sleep(); non dovrebbe essere usato, ma non riesco a capire perché sia ​​così. Se Thread.Sleep(); può causare problemi, ci sono soluzioni alternative con lo stesso risultato che sarebbe sicuro?

per esempio.

 while(true) { doSomework(); i++; Thread.Sleep(5000); } 

un altro è:

 while (true) { string[] images = Directory.GetFiles(@"C:\Dir", "*.png"); foreach (string image in images) { this.Invoke(() => this.Enabled = true); pictureBox1.Image = new Bitmap(image); Thread.Sleep(1000); } } 

I problemi con la chiamata Thread.Sleep sono spiegati in modo abbastanza sintetico qui :

Thread.Sleep ha il suo scopo: simulare lunghe operazioni durante il test / debug su un thread MTA. In .NET non c’è altro motivo per usarlo.

Thread.Sleep(n) significa bloccare il thread corrente per almeno il numero di timeslices (o quantum del thread) che può verificarsi in n millisecondi. La lunghezza di un timeslice è diversa su diverse versioni / tipi di Windows e diversi processori e generalmente varia da 15 a 30 millisecondi. Ciò significa che il thread è quasi garantito per bloccare più di n millisecondi. La probabilità che il thread si risveglierà esattamente dopo n millisecondi è imansible quanto imansible. Quindi Thread.Sleep è inutile per i tempi .

I thread sono una risorsa limitata, richiedono circa 200.000 cicli per creare e circa 100.000 cicli da distruggere. Per impostazione predefinita, riservano 1 megabyte di memoria virtuale per il proprio stack e utilizzano 2.000-8.000 cicli per ogni switch di contesto. Ciò rende ogni thread in attesa un enorme spreco.

La soluzione preferita: WaitHandles

L’errore più sbagliato è usare Thread.Sleep con un while-construct ( demo e risposta , bel blog-entry )

MODIFICARE:
Vorrei migliorare la mia risposta:

Abbiamo 2 diversi casi d’uso:

  1. Stiamo aspettando perché sappiamo un Thread.Sleep specifico in cui dovremmo continuare (utilizzare Thread.Sleep , System.Threading.Timer o simili)

  2. Stiamo aspettando perché alcune condizioni cambiano un po ‘di tempo … le parole chiave sono / sono un po’ di tempo ! se il condition-check è nel nostro dominio di codice, dovremmo usare WaitHandles – altrimenti il ​​componente esterno dovrebbe fornire qualche tipo di hook … se non lo fa il suo design è brutto!

La mia risposta riguarda principalmente il caso d’uso 2

SCENARIO 1 – attendere il completamento del task asincrono: Sono d’accordo che WaitHandle / Auto | ManualResetEvent debba essere utilizzato nello scenario in cui un thread è in attesa di attività su un altro thread da completare.

SCENARIO 2 – timing while loop: Tuttavia, come un meccanismo di crude timing (while + Thread.Sleep) è perfettamente soddisfacente per il 99% delle applicazioni che NON richiedono sapere esattamente quando il Thread bloccato dovrebbe “svegliarsi *. L’argomento che ci vuole Anche 200k cicli per creare il thread non sono validi – il thread del ciclo temporale deve essere creato comunque e 200k cicli è solo un altro grande numero (dimmi quanti cicli per aprire un file / socket / chiamate db?).

Quindi, se mentre + Thread.Sleep funziona, perché complicare le cose? Solo gli avvocati della syntax sarebbero pratici !

Vorrei rispondere a questa domanda dal punto di vista della politica della codifica, che può o non può essere utile a nessuno. Ma soprattutto quando hai a che fare con strumenti che sono destinati a 9-5 programmatori aziendali, le persone che scrivono documentazione tendono a usare parole come “non dovrebbe” e “mai” per significare “non fare questo a meno che tu non sappia veramente cosa tu stiamo facendo e perché “.

Un paio di altri miei preferiti nel mondo C # sono che ti dicono di “non chiamare mai lock (this)” o “mai chiamare GC.Collect ()”. Questi due sono dichiarati con forza in molti blog e documentazione ufficiale e l’IMO è completa disinformazione. A un certo livello, questa disinformazione ha il suo scopo, in quanto impedisce ai principianti di fare cose che non capiscono prima di ricercare completamente le alternative, ma allo stesso tempo rende difficile trovare informazioni REALI tramite motori di ricerca che tutti sembra che punti agli articoli che ti dicono di non fare qualcosa senza offrire alcuna risposta alla domanda “perché no?”

Politicamente, si riduce a quello che la gente considera “buon design” o “cattivo design”. La documentazione ufficiale non dovrebbe dettare il design della mia domanda. Se c’è davvero un motivo tecnico per cui non si dovrebbe chiamare sleep (), allora la documentazione IMO dovrebbe indicare che è del tutto normale chiamarla in scenari specifici, ma magari offrire alcune soluzioni alternative che sono indipendenti dallo scenario o più appropriate per l’altra scenari.

Chiaramente chiamare “sleep ()” è utile in molte situazioni in cui le scadenze sono chiaramente definite in termini reali, tuttavia, ci sono sistemi più sofisticati per aspettare e segnalare i thread che dovrebbero essere considerati e compresi prima di iniziare a tirare il sonno ( ) nel tuo codice e lanciare dichiarazioni sleep () inutili nel tuo codice è generalmente considerata una tattica per principianti.

È il 1) .spinning e 2) ciclo di esecuzione dei tuoi esempi a cui le persone mettono in guardia , non la parte Thread.Sleep (). Penso che Thread.Sleep () venga in genere aggiunto per migliorare facilmente il codice che gira o in un ciclo di polling, quindi è solo associato al codice “cattivo”.

Inoltre le persone fanno cose come:

 while(inWait)Thread.Sleep(5000); 

dove la variabile inWait non è accessibile in modo thread-safe, che causa anche problemi.

Quello che i programmatori vogliono vedere sono i thread controllati dai costrutti Eventi e Segnalazione e Blocco, e quando lo farai non avrai bisogno di Thread.Sleep (), e verranno eliminate anche le preoccupazioni sull’accesso alle variabili thread-safe. Ad esempio, potresti creare un gestore di eventi associato alla class FileSystemWatcher e utilizzare un evento per triggersre il tuo secondo esempio anziché il ciclo?

Come ha detto Andreas N., leggere Threading in C #, di Joe Albahari , è davvero molto buono.

Lo stato di sospensione viene utilizzato nei casi in cui programmi indipendenti su cui non si ha alcun controllo possono talvolta utilizzare una risorsa comunemente utilizzata (ad esempio un file), a cui il programma deve accedere quando viene eseguita e quando la risorsa viene utilizzata da questi altri programmi il tuo programma è bloccato dall’utilizzarlo. In questo caso, dove si accede alla risorsa nel proprio codice, si inserisce l’accesso della risorsa in un try-catch (per rilevare l’eccezione quando non è ansible accedere alla risorsa) e lo si inserisce in un ciclo while. Se la risorsa è gratuita, il sonno non viene mai chiamato. Ma se la risorsa è bloccata, si dorme per un periodo di tempo appropriato e si tenta di accedere nuovamente alla risorsa (questo perché si sta eseguendo il ciclo). Tuttavia, tieni presente che è necessario inserire una sorta di limitatore nel ciclo, quindi non è un loop potenzialmente infinito. È ansible impostare la condizione di limitazione in N numero di tentativi (questo è quello che uso di solito), oppure controllare l’orologio di sistema, aggiungere un tempo fisso per ottenere un limite di tempo e uscire dal tentativo di accesso se si raggiunge il limite di tempo.

Ho trovato un modo per hackerare Thread.Sleep facendo in modo che il programma si Thread.Sleep periodicamente e faccia eventi. Vedi sotto:

 // Pay attention every 1/10 sec to maintain some UI responsiveness while (val > 0) { Thread.Sleep(100); val = val - 100; Application.DoEvents(); if (val == x) { // You could do some conditional stuff here at specific times } } 

Sostituisci val con il ritardo desiderato in millisecondi. Più si abbassa il valore ms di Thread.Sleep più il programma sembrerà reattivo. 100ms mi sembrano accettabili. 50ms è il migliore

Sono d’accordo con molti qui, ma penso anche che dipenda.

Recentemente ho fatto questo codice:

 private void animate(FlowLayoutPanel element, int start, int end) { bool asc = end > start; element.Show(); while (start != end) { start += asc ? 1 : -1; element.Height = start; Thread.Sleep(1); } if (!asc) { element.Hide(); } element.Focus(); } 

Era una semplice funzione di animazione, e ho usato Thread.Sleep su di esso.

La mia conclusione, se lo fa, usalo.

Per quelli di voi che non hanno visto un argomento valido contro l’uso di Thread.Sleep in SCENARIO 2, c’è davvero uno – l’uscita dell’applicazione viene mantenuta dal ciclo while (SCENARIO 1/3 è semplicemente stupido quindi non degno di più menzionare)

Molti che fingono di essere informati, urlanti Thread.Sleep è male non hanno menzionato un singolo motivo valido per quelli di noi che hanno preteso una ragione pratica per non usarlo – ma eccolo, grazie a Pete – Thread.Sleep è Evil (può essere facilmente evitato con un timer / gestore)

  static void Main(string[] args) { Thread t = new Thread(new ThreadStart(ThreadFunc)); t.Start(); Console.WriteLine("Hit any key to exit."); Console.ReadLine(); Console.WriteLine("App exiting"); return; } static void ThreadFunc() { int i=0; try { while (true) { Console.WriteLine(Thread.CurrentThread.ThreadState.ToString() + " " + i); Thread.Sleep(1000 * 10); i++; } } finally { Console.WriteLine("Exiting while loop"); } return; }