2013-10-10 4 views
1

Quelle est la pratique générale pour créer un System.Timer imbriqué? Fondamentalement, j'ai besoin d'un minuteur parent qui fonctionnera indéfiniment toutes les 10 secondes, et un minuteur enfant qui fonctionnera une durée déterminée chaque seconde. La chose qui me pose problème est de savoir comment quitter correctement le minuteur une fois terminé.Comment créer un System.Timer imbriqué dans C#

Voici un exemple simple - J'ai un minuteur parent avec un ElapsedEventHandler correspondant. Dans cet événement, je lance un appel à une API tierce qui renvoie une liste d'objets. Maintenant que j'ai ces objets, j'ai besoin de faire des appels API supplémentaires sur chacun de ces objets, un appel toutes les secondes (entrer le minuteur enfant). Supposons que le premier appel d'API ramène 30 objets. Je veux que le ElapsedEventHandler pour la minuterie enfant ne fonctionne que 30 fois, puis quitte. Je ne suis pas sûr de savoir comment mettre en œuvre cela.

La raison de mes minuteurs est que cette API tierce impose une limite de vitesse.

+2

* Général * La pratique consiste à n'avoir qu'un seul temporisateur. Reste de "temporisateurs" peuvent résider à l'intérieur et utiliser des compteurs pour diviser la fréquence. – Sinatr

+0

Si vous devez faire 1 appel par objet, alors pourquoi ne pas itérer la liste des objets et les appeler. – Kami

+0

@Sinatr Que voulez-vous dire quand vous dites "reste de minuteries peuvent résider à l'intérieur"? –

Répondre

1

Avez-vous besoin d'aller chercher toutes les 10 secondes, ou lorsque toutes les données ont été traitées? Vous parlez de récupérer des données toutes les 10 secondes, mais de récupérer 30 éléments. À ce rythme, vous continuerez à allouer de la mémoire car il ne sera jamais capable de se rattraper.

1 seconde, puis incrémenter un compteur statique pour extraire les données toutes les 10 itérations. Une fois les données récupérées, chaque élément est retiré de la pile toutes les secondes.

using System; 
using System.Collections.Generic; 
using System.Threading; 

namespace TimedFetch 
{ 
    class Program 
    { 
     static System.Threading.Timer workTimer; 
     static Stack<string> itemList; 
     public static AutoResetEvent fetchDataEvent; 
     static int eventCounter; 

     public class ExternalDatamanager 
     { 
      public void FetchData() 
      { 
       DateTime startTime = DateTime.Now; 

       do 
       { 
        FetchHeaderData(); 
        fetchDataEvent.WaitOne(); 
        Console.WriteLine("{0} FetchData Signaled! List size {1}", DateTime.Now.ToLongTimeString(), itemList.Count); 
       } while(true); 
      } 

      private static void FetchHeaderData() 
      { 
       // replace this with your method of retrieving data 
       Console.WriteLine("{0} FetchHeaderData", DateTime.Now.ToLongTimeString()); 
       DateTime timeObject = DateTime.Now; 
       for (int i = 0; i < 30; i++) 
       { 
        lock (itemList) 
        { 
         itemList.Push(timeObject.ToLongTimeString()); 
         timeObject = timeObject.AddSeconds(1); 
        } 
       } 
      } 
     } 

     static void Main(string[] args) 
     { 
      eventCounter = 0; 
      itemList = new Stack<string>(); 
      fetchDataEvent = new AutoResetEvent(false); 

      ExternalDatamanager dataManager = new ExternalDatamanager(); 
      Thread workerThread = new Thread(new ThreadStart(dataManager.FetchData)); 
      workerThread.Start(); 

      workTimer = new System.Threading.Timer(workTimer_Elapsed, null, 0, 1000); 
     } 

     // executes once a second 
     private static void workTimer_Elapsed(Object state) 
     { 
      Console.WriteLine("{0} Fire!", DateTime.Now.ToLongTimeString()); 
      Interlocked.Increment(ref eventCounter); 

      lock (itemList) 
      { 
       // every 10 seconds 
       if (itemList.Count > 0) 
        FetchDetail(itemList.Pop()); 
       // else ?? 
       // { ?? 
       if (eventCounter % 10 == 0) 
        fetchDataEvent.Set(); 
       // } ?? 
      } 
     } 

     private static void FetchDetail(string headerRecord) 
     { 
      Console.WriteLine("{0} Fetch detail for {1}", DateTime.Now.ToLongTimeString(), headerRecord); 
     } 
    } 
} 
0

Si l'appelant de l'API s'exécute sur un thread séparé, oubliez d'utiliser un minuteur enfant. Il suffit de faire dormir le fil pendant une seconde entre les tirages.

Quelque chose comme ceci:

foreach(var item in apiObjectsToGet) 
{ 
    api.GetItem(item); 
    Thread.Sleep(1000); 
} 
1

On dirait que vous avez juste besoin d'un traitement global de file d'attente avec une seule horloge mis en place pour tout intervalle d'accélération du vendeur de l'API vous impose. Toutes les demandes d'API seraient mises en file d'attente et les résultats de toutes les demandes d'API qui nécessiteraient d'autres requêtes enfants sont également mis en file d'attente. Vous avez un seul temporisateur qui traite l'élément suivant sur la file d'attente toutes les secondes.

Voici un exemple de ce dont je parle. Je créerais une sous-classe pour chaque requête d'API, afin qu'elle a) traite correctement la requête et b) met en file d'attente des sous-tâches si nécessaire, définissant l'état des tâches enfants à partir des résultats de la demande d'API actuelle:

void Main() 
{ 
    Timer t = new Timer(ProcessNextItem, null, 0, 1000); 
} 

public void ProcessNextItem(object o){ 
    var nextTask = Tasks.Take(); 

    nextTask.Process(); // Process does the API calls and enqueues new tasks onto Tasks, and sets state of child tasks as needed 
} 

public static BlockingCollection<APIRequestTask> Tasks = new BlockingCollection<APIRequestTask>(new ConcurrentQueue<APIRequestTask>()); 

public abstract class APIRequestTask{ 
    public void Process(){} 
    public object State {get;set;} 
} 
+0

Je serais en faveur de cette approche, cependant, où cela tombe serait si l'appel API suivant dépend du résultat de la précédente. – James

+0

Ensuite, vous passez les résultats de l'appel d'API en cours à tous ceux qui en ont besoin. Vous pouvez utiliser la propriété State dans APIRequestTask – DavidN

+0

ouais mais une approche en file d'attente implique que vous les ajoutez simplement à une file d'attente et que vous les oubliez. Il n'y a aucune notion de "* cet élément suivant a besoin de données de l'appel précédent *".Vous auriez besoin de construire ce genre de chose, pourrait être un peu en désordre. – James

Questions connexes