1

What's the general practice for creating a nested System.Timer? Basically, I need a parent timer that will run indefinitely every 10 seconds, and a child timer that will run a determined length of time every second. The thing I'm having a problem with is how to properly exit the child timer once it's finished.

Here's a simple example - I have a parent timer with a corresponding ElapsedEventHandler. Within this event I make a call to a third party API that returns a list of objects. Now that I have these objects, I need to make extra API calls on each of these objects, one call every second (enter the child timer). Say the first API call brings back 30 objects. I want the ElapsedEventHandler for the child timer to only run 30 times, then exit. I'm just not sure how to implement this.

The reason for my timers is because this third party API imposes a speed limit.

5
  • 2
    General practice is to have only one timer. Rest of "timers" can reside inside and use counters to divide the frequency. Commented Oct 10, 2013 at 14:05
  • If you need to make 1 call per object, then why not iterate the list of objects and call them. Commented Oct 10, 2013 at 14:06
  • @Sinatr What do you mean when you say "rest of timers can reside inside"? Commented Oct 10, 2013 at 14:08
  • 1
    @Kami "the reason for my timers is because this third party API imposes a speed limit" - I assume what the OP means by that is he can only make 1 API call every 1 second so he can't just iterate the list and fire them off as that would result in 30 calls within 1 second... Commented Oct 10, 2013 at 14:08
  • @MikeMarks, simple example, you need to have 1.5 sec timer1 and 10 sec timer2. Than you can create a single timer what will be triggered every 0.5 sec. Every third trigger you perform a job for timer1 and every 20th - timer2. Commented Oct 10, 2013 at 14:37

3 Answers 3

1

Do you need to fetch every 10 seconds, or when all of the data has been processed? You mention fetching data every 10 seconds, but getting 30 items back. At that rate, you'll keep allocating memory as it'll never be able to catch up.

1 second timer then increment a static counter to fetch data every 10 iterations. Once data is retrieved, each item is popped off the stack every second.

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);
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

Sounds like you just need a global processing Queue with a single timer set up for whatever throttle interval the API vendor imposes upon you. All API requests would be queued up, and the results of any API requests that would require further child requests are queued up as well. You have a single timer which processes the next item on the Queue every second.

Here's a sample of what I'm talking about. I'd create a sub-class for each API request, so that it will a) correctly process the request and b) enqueue sub-tasks as needed, setting state of child tasks from the results of the current API request if needed:

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

6 Comments

I would be in favour of this approach, however, where this falls over would be if the next API call depends on the result of the previous.
Then you pass the results of the current API call to all next ones that need it. You can use the State property in APIRequestTask
yeah but a queued approach implies you just add them to a queue and forget about them. There is no notion of "this next item needs data from the previous call". You would need to build that sort of thing in, could get a little messy.
Not as general as "this next item needs data from the previous call", but with the example above you could easily have "parent" tasks pass data to "child" tasks, which works for a lot of cases I've seen crop up. Decide for yourself if this fits your specific criteria.
It was in response to your question in the comments. I don't see how this proposed solution is as messy as you describe, and it may or may not suit your needs depending on the dependencies between subsequent API calls. That's all I was stating.
|
0

If the API caller is running on a separate thread, forget using a child timer. Just make the thread sleep for a second between pulls.

Something like this:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.