7
votes

Minuterie filetée unique

Je voulais une minuterie avec les propriétés suivantes:

  1. Peu importe combien de fois le démarrage est appelé, un seul fil de rappel est toujours en cours d'exécution p> li>

  2. Le temps passé dans la fonction de rappel a été ignoré en ce qui concerne l'intervalle. E.g Si l'intervalle est de 100 ms et que le rappel prend 4 000 ms pour exécuter, le rappel est appelé à 100 ms, 4100ms, etc. p> li> ol>

    Je n'ai pas pu voir quoi que ce soit disponible, a écrit le code suivant. Y a-t-il une meilleure façon de faire cela? P>

    /**
     * Will ensure that only one thread is ever in the callback
     */
    public class SingleThreadedTimer : Timer
    {
        protected static readonly object InstanceLock = new object();
        
        //used to check whether timer has been disposed while in call back
        protected bool running = false;
    
        virtual new public void Start()
        {
            lock (InstanceLock)
            {
                this.AutoReset = false;
                this.Elapsed -= new ElapsedEventHandler(SingleThreadedTimer_Elapsed);
                this.Elapsed += new ElapsedEventHandler(SingleThreadedTimer_Elapsed);
                this.running = true;
                base.Start();
            }
            
        }
    
        virtual public void SingleThreadedTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            lock (InstanceLock)
            {
                DoSomethingCool();
    
                //check if stopped while we were waiting for the lock,
                //we don't want to restart if this is the case..
                if (running)
                {
                    this.Start();
                }
            }
        }
    
        virtual new public void Stop()
        {
            lock (InstanceLock)
            {
                running = false;
                base.Stop();
            }
        }
    }
    


5 Réponses :


8
votes

Voici un exemple rapide que je viens de frapper; xxx


3 commentaires

Bonne réponse et merci. Je me promenais ce qui se produirait si deux threads ont appelé la routine de départ simultanément?


@RICHARD FAIR POINT, la réponse ne fonctionnerait de manière fiable que pour une seule application filetée. J'ai édité ma réponse pour donner une idée de l'endroit où le verrouillage entrerait en jeu. Vous pouvez toujours verrouiller (ceci) à la place si vous préférez.


@Richard: Pour vous assurer qu'un seul thread d'appelera la routine de départ à un moment donné, vous pouvez utiliser Mutex. Vous trouverez plus de détails ici MSDN.MicRosoft.com/en-us/Library/ MS173179.aspx



1
votes

Regardez le [ThreadStatic] Attribut et le type générique ThreadLocal .NET 4.0. Cela vous donnera probablement rapidement un moyen de coder cela sans gâcher avec le verrouillage du fil, etc.

Vous pouvez avoir une pile à l'intérieur de votre classe de temps et que vous pourriez implémenter une méthode du moniteur () qui renvoie une option isolable. Vous pouvez ainsi utiliser le la minuterie comme: xxx

avez le moniteur de la minuterie enfoncé l'horodatage d'intervalle éteint la pile pendant le disposer ().

codant manuellement tout le verrouillage et La reconnaissance du fil est une option comme cela a été mentionné. Cependant, le verrouillage influencera le temps utilisé, probablement plus que de devoir initialiser une instance par fil à l'aide de ThreadLocal

si vous êtes intéressé, je pourrais assommer un exemple plus tard


0 commentaires

3
votes

Le .NET Framework fournit Quatre minuteries . Deux d'entre eux sont multithreads à usage général TIMERS:

  • System.threading.Timer
  • System.Timers.Timer

    Les deux autres sont spéciales minuteries à filetage unique :

    • System.Windows.Forms.Timer (Minuterie Windows Formulaires)
    • system.windows.threading.dispatchertimer (minuterie WPF)

      Les 2 derniers sont conçus pour éliminer les problèmes de sécurité pour les applications WPF et Windows Formulaires.

      Par exemple, en utilisant WebBrowser à l'intérieur d'une minuterie pour capturer des captures d'écran à partir de la page Web doit être unifaminé et donne une erreur au moment de l'exécution s'il s'agit d'un autre fil.

      Les minuteries à threads unique ont les suivants avantages

      • Vous pouvez oublier la sécurité du thread.
      • Une nouvelle tique ne tirera jamais avant que la ticke précédente ait fini traitement.
      • Vous pouvez mettre à jour les éléments d'interface utilisateur et les contrôles directement à partir de Code de traitement des événements de tick, sans appeler contrôle.begininvoke ou Dispatcher.Beginin Voke.

        et principal désavantage à noter

        • Un thread sert toutes les minuteries - ainsi que les événements de traitement de l'interface utilisateur. Ce qui signifie que le gestionnaire d'événements de tiques doit exécuter rapidement, Sinon, l'interface utilisateur devient insensible.

          source : la plupart sont des restes de C # en un mot Book -> Chapitre 22 -> Filetage avancé -> Minuères -> Mintress à une seule-filetage


0 commentaires

4
votes

Pour quiconque a besoin d'une seule minuterie de thread et que la minuterie commence à cocher après la tâche . System.Timers.Timer pourrait faire le tour sans verrouillage ni [threadstatique] xxx

crédit sur Alan N Source https://www.codeproject.com / Réponses / 405715 / System-Timers-Timers-Offre-usage-usage # Réponse2

Edit: Espacement


0 commentaires

0
votes

Voici un simple périodiquenonoverlappeMer code>, qui fournit uniquement les fonctionnalités demandées, et rien de plus que cela. Cette minuterie ne peut être démarrée et arrêtée à la demande, et aucun intervalle n'a été modifié. Il invoque simplement l'action spécifiée périodiquement d'une manière non superposée, jusqu'à ce que la minuterie soit disposée. XXX PRE>

Exemple d'utilisation. Montre comment créer une minuterie non superposé qui commence immédiatement, avec une période de 10 secondes. P> xxx pré>

au cas où le DosomoolCool code> échoue, l'exception sera lancé sur le threadpool code> , causant le processus de crash. Donc, vous voudrez peut-être ajouter un essayer code> / bloc code> et gérer toutes les exceptions pouvant survenir. P>

Le Dispose code > est une méthode potentiellement bloquante. Si la périodicaction code> est en cours d'exécution, le Dispose code> bloquera jusqu'à la fin de la dernière invocation. Si vous ne voulez pas attendre que cela se produise, vous pouvez le faire à la place: P>

_ = timer.DisposeAsync(); // Stop the timer without waiting it to finish


0 commentaires