Domanda

Ho un metodo che dovrebbe essere ritardata 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);

Avevo letto alcune articoli sull'utilizzo Thread.Sleep è cattivo design. Ma io non capisco perché.

Ma per l'utilizzo di Timer, Timer ha metodo di smaltimento. Dal momento che l'esecuzione è in ritardo, non so come smaltire Timer. Avete qualche suggerimento?

O se avete codici alternativi per l'esecuzione ritardata sono anche apprezzare.

È stato utile?

Soluzione

Una differenza è che System.Threading.Timer invia la richiamata su un thread pool di thread, invece di creare un nuovo thread ogni volta. Se è necessario che questo accada più di una volta durante la vita della vostra applicazione, questo salverà il sovraccarico di creare e distruggere un fascio di fili (un processo che è molto alta intensità di risorse, come l'articolo si fa riferimento fa notare), dal momento che basta riutilizzare thread nel pool, e se si vuole avere più di un timer di andare in una sola volta che significa che si avrà un minor numero di thread in esecuzione in una sola volta (anche risparmio di notevoli risorse).

In altre parole, Timer sta per essere molto più efficiente. Può anche essere più precisi, dal momento che Thread.Sleep è garantita solo di aspettare almeno fino a quando la quantità di tempo specificato (il sistema operativo può metterlo a dormire per molto più tempo). Certo, non è ancora Timer sta per essere esattamente accurata, ma l'intento è quello di sparare il callback più vicino possibile al tempo specificato come possibile, mentre questo non è necessariamente l'intento di Thread.Sleep.

Per quanto riguarda la distruzione del Timer, il callback può accettare un parametro, in modo da essere in grado di passare il Timer stesso come il parametro e chiamare Dispose nel callback (anche se non ho provato questo - credo che sia possibile che il timer potrebbe essere bloccato durante il callback).

Modifica:. No, credo che non si può fare questo, poiché è necessario specificare il parametro di richiamata nel Timer costruttore stesso

Forse qualcosa di simile? (Di nuovo, non hanno effettivamente 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 bloccaggio dovrebbe impedire la richiamata timer dal tentativo di liberare il timer prima del campo Timer sia stato predisposto.


Addendum: Come il commentatore ha sottolineato, se action() fa qualcosa con l'interfaccia utente, quindi utilizzando un System.Windows.Forms.Timer è probabilmente una scommessa migliore, dal momento che verrà eseguito il callback sul thread dell'interfaccia utente. Tuttavia, se questo non è il caso, e si è scesi a Thread.Sleep vs Threading.Timer, Threading.Timer è la strada da percorrere.

Altri suggerimenti

ThreadPool.RegisterWaitForSingleObject , invece di temporizzatore:

//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);

Credo Thread.Sleep va bene se si vuole veramente mettere in pausa l'applicazione per un periodo di tempo specificato. Penso che il motivo la gente dice che è una cattiva progettazione è perché nella maggior parte dei casi la gente non vuole in realtà l'applicazione per mettere in pausa.

Per esempio, stavo lavorando su un client POP3 in cui il programmatore stava usando Thread.Sleep (1000) di aspettare mentre la presa recuperate posta. In quella situazione era meglio collegare un gestore di eventi per la presa e continuare l'esecuzione del programma, dopo la presa aveva completato.

Mi ricordo l'implementazione di una soluzione simile a quella di Eric. Questo è comunque uno funzionante;)

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'unica carne che ho con la System.Timer è che la maggior parte del tempo ho visto utilizzato per lunghi ritardi (ore, minuti) in Servizi di sondaggi e gli sviluppatori spesso si dimentica di lanciare l'evento Prima si avvia il timer. Questo significa che se mi metto l'applicazione o il servizio, devo aspettare fino a quando il timer è trascorso (ore, minuti) prima di eseguire in realtà.

Certo, questo non è un problema con il timer, ma penso che la sua spesso usato impropriamente per perché la sua semplicemente troppo facile da uso improprio.

@miniscalope No non utilizzare ThreadPool.RegisterWaitForSingleObject invece di timer, System.Threading.Timer in coda un callback da eseguire su un thread pool di thread quando il tempo è trascorso e non richiede una maniglia di attesa, attendere singolo oggetto verrà impegnare un thread di thread in attesa dell'evento da segnalare o il timeout scada prima del thread chiama la richiamata.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top