Confronta usando Thread. Sleep e Timer per l’esecuzione ritardata

Ho un metodo che dovrebbe essere ritardato in esecuzione per un determinato periodo di tempo.

Dovrei usare

Thread thread = new Thread(() => { Thread.Sleep(millisecond); action(); }); thread.IsBackground = true; thread.Start(); 

O

 Timer timer = new Timer(o => action(), null, millisecond, -1); 

Ho letto alcuni articoli sull’utilizzo di Thread.Sleep è un cattivo design. Ma non capisco davvero perché.

Ma per usare Timer, Timer dispone di un metodo. Poiché l’esecuzione è ritardata, non so come smaltire il timer. Hai qualche suggerimento?

Oppure se si dispone di codici alternativi per l’esecuzione ritardata sono anche apprezzati.

Una differenza è che System.Threading.Timer invia il callback su un thread pool di thread, invece di creare un nuovo thread ogni volta. Se hai bisogno che questo accada più di una volta durante la vita della tua applicazione, questo salverà il sovraccarico di creare e distruggere un gruppo di thread (un processo che richiede molto risorse, come fa notare l’articolo che fai riferimento), poiché riusate i thread nel pool e se avrete più di un timer in una volta significa che avrete un minor numero di thread in esecuzione contemporaneamente (risparmiando anche considerevoli risorse).

In altre parole, Timer sarà molto più efficiente. Potrebbe anche essere più accurato, poiché Thread.Sleep è garantito solo per attendere almeno il tempo specificato (il sistema operativo potrebbe metterlo in stato di sospensione per molto più tempo). Certo, il timer non sarà ancora esattamente preciso, ma l’intento è quello di triggersre la richiamata il più vicino ansible all’orario specificato, mentre questo NON è necessariamente l’intento di Thread.Sleep.

Per quanto riguarda la distruzione del Timer, il callback può accettare un parametro, quindi potresti essere in grado di passare il Timer stesso come parametro e chiamare Dispose nel callback (anche se non l’ho provato – immagino sia ansible che il Timer potrebbe essere bloccato durante la richiamata).

Modifica: No, immagino che non puoi farlo, dal momento che devi specificare il parametro di callback nel costruttore Timer stesso.

Forse qualcosa del genere? (Di nuovo, non l’ho ancora provato)

 class TimerState { public Timer Timer; } 

… e per avviare il timer:

 TimerState state = new TimerState(); lock (state) { state.Timer = new Timer((callbackState) => { action(); lock (callbackState) { callbackState.Timer.Dispose(); } }, state, millisecond, -1); } 

Il blocco dovrebbe impedire al callback del timer di provare a liberare il timer prima che sia stato impostato il campo Timer.


Addendum: come sottolineato dal commentatore, se action () fa qualcosa con l’interfaccia utente, quindi usare System.Windows.Forms.Timer è probabilmente una scelta migliore, poiché eseguirà la richiamata sul thread dell’interfaccia utente. Tuttavia, se questo non è il caso, ed è giù per Thread.Sleep vs. Threading.Timer, Threading.Timer è la strada da percorrere.

usa ThreadPool.RegisterWaitForSingleObject posto del timer:

 //Wait 5 seconds then print out to console. //You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true); 

Penso che Thread.Sleep va bene se vuoi davvero mettere in pausa l’applicazione per un periodo di tempo specificato. Penso che la ragione per cui le persone dicono che sia un cattivo design è perché nella maggior parte delle situazioni le persone non vogliono realmente che l’applicazione si fermi.

Ad esempio, stavo lavorando a un client pop3 in cui il programmatore stava utilizzando Thread.Sleep (1000) per attendere che il socket recuperi la posta. In tale situazione era preferibile colbind un gestore di eventi al socket e continuare l’esecuzione del programma dopo che il socket era stato completato.

Ricordo di aver implementato una soluzione simile a quella di Eric. Questo è comunque un lavoro;)

 class OneTimer { // Created by Roy Feintuch 2009 // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context public static void DoOneTime(ThreadStart cb, TimeSpan dueTime) { var td = new TimerDisposer(); var timer = new Timer(myTdToKill => { try { cb(); } catch (Exception ex) { Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]"); } finally { ((TimerDisposer)myTdToKill).InternalTimer.Dispose(); } }, td, dueTime, TimeSpan.FromMilliseconds(-1)); td.InternalTimer = timer; } } class TimerDisposer { public Timer InternalTimer { get; set; } } 

L’unico problema che ho con System.Timer è che la maggior parte delle volte l’ho visto utilizzato per lunghi ritardi (ore, minuti) nei servizi di polling e gli sviluppatori spesso dimenticano di lanciare l’evento prima di avviare il timer. Ciò significa che se avvii l’app o il servizio, devo attendere che il timer scada (ore, minuti) prima che venga effettivamente eseguito.

Certo, questo non è un problema con il timer, ma penso che sia spesso usato impropriamente perché è troppo facile da abusare.

@miniscalope No non utilizzare ThreadPool.RegisterWaitForSingleObject al posto del timer, System.Threading.Timer accoderà un callback da eseguire su un thread pool di thread quando il tempo è scaduto e non richiede un handle di attesa, attendere che il singolo object bind un thread del threadpool in attesa che l’evento venga segnalato o che scada il timeout prima che il thread richiami la richiamata.