2010-08-30 7 views
3

Il y a des moments dans mon application, lorsque j'ai besoin d'invoquer ma minuterie manuellement.Indiquer à l'objet timer d'invoquer son événement "Elapsed" de manière asynchrone

J'ai essayé ce qui suit:

int originalInterval = t.Interval; 
t.Interval = 0; 
t.Interval = originalInterval; 

mais il ne correspondait pas.

J'ai créé un nouveau temporisateur, héritant de System.Timers.Timer, et exposé une méthode «Tick» - mais le problème était que l'événement «Elapsed» a ensuite été déclenché de manière synchrone.

Lorsque j'ai implémenté le "Tick" avec un nouveau thread - les résultats étaient, encore une fois, pas cohérent.

Existe-t-il une meilleure façon de l'implémenter?

+0

En quoi les résultats ne sont-ils pas cohérents? –

+0

1. Le thread a été fermé en raison de l'utilisation de Thread.Start, au lieu de ThreadPool.Queue (que je ne voulais pas utiliser ... trop moche) 2. Lorsque l'utilisateur invoqué "Tick" plusieurs fois (avant le méthode arrêtée) - Je voulais attendre le premier appel pour finir à chaque fois et l'invoquer seulement alors – Fliskov

Répondre

4

J'ai eu le même problème, donc j'utilisé le AutoResetEvent savoir si le succès a été invoqué Elapsed:

/// <summary> 
/// Tickable timer, allows you to manually raise a 'Tick' (asynchronously, of course) 
/// </summary> 
public class TickableTimer : System.Timers.Timer 
{ 
    public new event ElapsedEventHandler Elapsed; 

    private System.Threading.AutoResetEvent m_autoResetEvent = new System.Threading.AutoResetEvent(true); 

    public TickableTimer() 
     : this(100) 
    { 
    } 

    public TickableTimer(double interval) 
     : base(interval) 
    { 
     base.Elapsed += new ElapsedEventHandler(TickableTimer_Elapsed); 
    } 

    public void Tick() 
    { 
     new System.Threading.Thread(delegate(object sender) 
     { 
      Dictionary<string, object> args = new Dictionary<string, object> 
      { 
       {"signalTime", DateTime.Now}, 
      }; 
      TickableTimer_Elapsed(this, Mock.Create<ElapsedEventArgs>(args)); 
     }).Start(); 
     this.m_autoResetEvent.WaitOne(); 
    } 

    void TickableTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     m_autoResetEvent.Set(); 
     if (this.Elapsed != null) 
      this.Elapsed(sender, e); 
    } 
} 
+0

Grand mec !!! Bien fait! (en utilisant le 'Mock' est génial!) – Fliskov

+0

J'utilise 'Moke' parce que son constructeur est privé – Nissim

+0

quel espace de noms inclut Mock? –

0

Normalement, vous ne devriez pas avoir besoin de tirer une minuterie manuellement - vous pouvez toujours courir le événement lui-même dans un nouveau fil. En gros, c'est essentiellement ce que fait le minuteur, et puisque vous voulez le déclencher manuellement, vous n'avez pas besoin du minuteur (pour cette invocation manuelle).

Vous n'avez spécifié aucun détail quant à ce que vous entendez par "non conforme". Ce qui suit devrait normalement travailler:

Thread thread = new Thread(myDelegate); 
thread.Start(); 

Bien sûr, myDelegate peut être un lambda dans le cas où vous avez besoin de passer des paramètres:

Thread thread = new Thread(() => myMethod(param1, param2)); 
thread.Start(); 
+0

J'ai essayé - mais les résultats étaient toujours incompatibles ... surtout lorsque l'utilisateur appelé « Tick » à plusieurs reprises – Fliskov

+0

@Fliskov: dire « il est incompatible » est plus utile maintenant que c'était dans votre question. Un détail? – Timwi

+0

Regardez le commentaire à @Fredreck – Nissim

3

Il se sent comme vous devriez regarder votre conception un peu. En général, j'essaie d'éviter que la méthode du gestionnaire d'événements ne contienne le travail en cours, mais j'essaie plutôt de le laisser juste un déclencheur, en appelant une autre méthode qui effectue le travail. De cette façon, vous pouvez invoquer cette autre méthode de nulle part ailleurs ainsi:

private void Timer_Tick(object sender, EventArgs e) 
{ 
    new Thread(MethodThatDoesTheWork).Start(); 
} 

private void MethodThatDoesTheWork() 
{ 
    // actual work goes here 
} 

Maintenant, vous pouvez appeler MethodThatDoesTheWork de nulle part ailleurs dans la classe (soit en utilisant de façon synchrone ou asynchrone un thread séparé).

Par ailleurs, si MethodThatDoesTheWork devrait toujours être un appel asynchrone, vous pouvez engendrez le fil dans cette méthode à la place:

private void MethodThatDoesTheWork() 
{ 
    new Thread(() => 
    { 
     // work code goes here 
    }).Start(); 
} 

Dans ces échantillons, j'ai fils créés manuellement. Vous pouvez utiliser cette approche, ThreadPool, Task ou toute autre méthode d'appel de code de manière asynchrone, selon ce qui convient le mieux dans votre contexte.

+0

Comme ma question implicite - en utilisant Thread.Start ne résout pas le problème – Fliskov

+0

@Fliskov: votre question indique que l'invocation de l'événement 'Tick' sur un fil séparé ne résout pas le problème. Ma suggestion est d'éviter de l'invoquer du tout. Peut-être pourriez-vous élaborer un peu sur ce que fait votre code dans la question (cela faciliterait la réponse utile)? –