Question

J'ai une méthode qui devrait être retardé en cours d'exécution pour un laps de temps spécifié.

Dois-je utiliser

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

ou

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

J'avais lu certains articles sur l'utilisation Thread.Sleep est mauvaise conception. Mais je ne comprends pas vraiment pourquoi.

Mais pour l'utilisation de la minuterie, la minuterie a méthode dispose. Depuis l'exécution est retardée, je ne sais pas comment disposer la minuterie. Avez-vous des suggestions?

Ou si vous avez des codes alternatifs pour l'exécution différée sont apprécierons également.

Était-ce utile?

La solution

Une différence est que System.Threading.Timer envoie le rappel sur un thread pool de threads, plutôt que de créer un nouveau fil à chaque fois. Si vous avez besoin que cela se produise plus d'une fois pendant la durée de votre application, cela permettra d'économiser les frais généraux de la création et la destruction d'un tas de fils (un processus qui est ressource très intense, comme l'article que vous faites référence souligne), car il sera simplement réutiliser des fils dans la piscine, et si vous avez plus d'une minuterie va à la fois cela signifie que vous aurez moins de threads en cours d'exécution à la fois (en économisant des ressources considérables).

En d'autres termes, Timer va être beaucoup plus efficace. Il peut également être plus précis, puisque Thread.Sleep est garanti que d'attendre au moins aussi longtemps que la quantité de temps que vous spécifiez (le système d'exploitation peut le mettre à dormir beaucoup plus longtemps). Certes, Timer est toujours ne va pas être tout à fait exact, mais l'intention est de tirer le rappel au plus près du temps spécifié possible, alors que ce n'est pas nécessairement l'intention de Thread.Sleep.

Quant à détruire le Timer, le rappel peut accepter un paramètre, vous pouvez donc être en mesure de passer le Timer lui-même comme paramètre et appeler Dispose dans le rappel (bien que je ne l'ai pas essayé - je suppose qu'il est possible que la minuterie peut être verrouillé pendant le rappel).

Edit:. Non, je suppose que vous ne pouvez pas faire cela, puisque vous devez spécifier le paramètre de rappel dans le constructeur de Timer lui-même

Peut-être quelque chose comme ça? (Encore une fois, ne sont pas réellement essayé)

class TimerState
{
    public Timer Timer;
}

... et pour lancer le chronomètre:

TimerState state = new TimerState();

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

Le verrouillage doit empêcher le rappel de la minuterie d'essayer de libérer la minuterie avant le champ Timer ayant été défini.


Addendum: Comme l'intervenant a souligné, si action() fait quelque chose avec l'interface utilisateur, puis en utilisant un System.Windows.Forms.Timer est probablement un meilleur pari, car il exécutera le rappel sur le thread d'interface utilisateur. Toutefois, si ce n'est pas le cas, et il est jusqu'à Thread.Sleep contre Threading.Timer, Threading.Timer est le chemin à parcourir.

Autres conseils

ThreadPool.RegisterWaitForSingleObject au lieu de la minuterie:

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

Je pense que Thread.Sleep est très bien si vous voulez vraiment mettre en pause l'application d'un certain laps de temps. Je pense que la raison des gens disent qu'il est une mauvaise conception est parce que dans la plupart des cas les gens ne veulent pas vraiment l'application pour faire une pause.

Par exemple, je travaillais sur un client pop3 où le programmeur utilisait Thread.Sleep (1000) attendre que le socket récupéré courrier. Dans cette situation, il est préférable de brancher un gestionnaire d'événements à la prise et de poursuivre l'exécution du programme après la prise avait terminé.

Je me souviens mettre en œuvre une solution similaire à celui de Eric. Ceci est cependant un travail;)

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; }
    }

Le seul boeuf que je le System.Timer est que la plupart du temps, je l'ai vu utilisé pour de longs retards (heures, minutes) dans les services de vote et les développeurs oublient souvent de lancer l'événement Avant ils commencent la minuterie. Cela signifie que si je commence à l'application ou d'un service, je dois attendre jusqu'à ce que la minuterie soit écoulé (heures, minutes) avant d'exécuter réellement.

Bien sûr, ce n'est pas un problème avec la minuterie, mais je pense que son souvent mal utilisé par parce que son trop facile à une mauvaise utilisation.

Non @miniscalope ne pas utiliser ThreadPool.RegisterWaitForSingleObject au lieu de la minuterie, System.Threading.Timer en file d'attente d'un rappel à exécuter sur un thread pool de fil lorsque le temps est écoulé et ne nécessite pas une poignée d'attente, attendez un seul objet sera attacher un fil ThreadPool attendant l'événement à signaler ou que le délai d'expiration avant que le thread appelle le rappel.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top