Reset System.Timers.Timer to prevent Elapsed event

23,545

You can use the Stop function followed immediately by the Start function to "restart" the timer. Using that you can create the Timer when the class is first created, wire up the Elapsed event at that time, and then do nothing but call those two methods when an item is added. It will either start, or restart, the timer. Note that calling Stop on a timer that hasn't yet been started just does nothing, it doesn't throw an exception or cause any other problems.

public class Foo
{
    public static List<string> list;
    public static Timer timer;
    static Foo()
    {
        list = new List<string>();
        timer = new Timer(10000);
        timer.Enabled = true;
        timer.AutoReset = false;
        timer.Elapsed += SendToServer;
    }

    public static void Log(string value)
    {
        list.Add(value);
        timer.Stop();
        timer.Start();
    }

    static void SendToServer(object sender, ElapsedEventArgs e)
    {
        //TODO send data to server

        //AutoReset is false, so neither of these are needed
        //timer.Enabled = false;
        //timer.Stop();
    }
}

Note that rather than using a List it's very possible that you want to use a BlockingCollection<string> instead. This has several advantages. First, the Log methods will work if called at the same time from multiple threads; as is multiple concurrent logs could break the list. It also means that SendToServer can be taking items out of the queue at the same time that new items are added. If you use a List you'll need to lock all access to the list (which might not be a problem, but isn't as straightforward).

Share:
23,545
tedski
Author by

tedski

Updated on May 16, 2020

Comments

  • tedski
    tedski almost 4 years

    I am trying to use the Timer to trigger an event to send data across the network. I created a simple class to debug. Basically I have a List<string> I'd like to send. I want the following to happen:

    1. Add string to List
    2. Start Timer for 10 seconds
    3. Add second string to List before Timer.Elapsed
    4. Restart Timer back at 10 seconds.

    So far I have this:

    public static List<string> list;
    public static Timer timer;
    public static bool isWiredUp = false;
    
    public static void Log(string value) {
        if (list == null) list = new List<string>();
        list.Add(value);
    
        //this does not reset the timer, elapsed still happens 10s after #1
        if (timer != null) {
            timer = null;
        }
    
        timer = new Timer(10000);
        timer.Start();
        timer.Enabled = true;
        timer.AutoReset = false;
    
        if (!isWiredUp) {
            timer.Elapsed += new ElapsedEventHandler(SendToServer);
            isWiredUp = true;
        }
    }
    
    static void SendToServer(object sender, ElapsedEventArgs e) {
        timer.Enabled = false;
        timer.Stop();
    }
    

    Any ideas?

  • Andrew Savinykh
    Andrew Savinykh about 11 years
    Your link shows "This topic is obsolete." and does not contain useful information.
  • Andrew Savinykh
    Andrew Savinykh about 11 years
    From the question I understand that he wants to accumulate data in the list and send them once only after 10 seconds since last addition to list has elapsed. He wants to avoid SendToServer being called twice if he adds data to the list two times three seconds apart. I could be wrong, but that's how I read it.
  • Servy
    Servy about 11 years
    @zespri That's exactly how I read it to. That is exactly what this code does.
  • Matthew Sanford
    Matthew Sanford about 11 years
    even using a timer.. this is a bad idea.. There is no locking, no form of synchronization.. A timer is the absolute worst way to go about doing this, he should reconsider how he is solving this problem.
  • tedski
    tedski about 11 years
    The only problem is when the Start() function is called a second time, it doesn't delay the Elapsed event.
  • tedski
    tedski about 11 years
    I have no idea what you are talking about or what that page is supposed to be telling me
  • Servy
    Servy about 11 years
    @MatthewSanford The synchronization needs to be done in addition to use a Timer, and is an entirely separate issue from the question at hand. Yes, he needs to address that issue, but that's not what the question is asking about.
  • tedski
    tedski about 11 years
    From debugging the time of adding each item, I've found that the Elapsed event fires exactly 10s after the first .Start(), regardless of .Start() being called again.
  • Servy
    Servy about 11 years
    @tedski Sorry, that functionality applies to other types of Timer classes. For this class you just need to call Stop and then start, which is still a simple enough change. See edit.
  • tedski
    tedski about 11 years
    Awesome, that answered my question. I am going to have to look into what @MatthewSanford was talking about now.
  • tedski
    tedski about 11 years
    Thanks, this looks more digestible. I accepted the other answer for now, I have a meeting but will look over your response after.
  • Servy
    Servy about 11 years
    @tedski See the last paragraph of my post which addresses those issues. Just use a BlockingCollection instead of a list and you should be fine.
  • Servy
    Servy about 11 years
    This code doesn't do what is requested in the OP, and honestly isn't a particularly good implementation of the producer/consumer model. Your code is set to send data every 10 seconds, regardless of when data is added. He wants to send data 10 seconds after the last time data was added to the list (resetting any time new data is added). Your code doesn't do that. Next, your consumer is creating a new thread just to sit around doing nothing almost all of the time; that's wasting a lot of resources. Financially, the synchronization mechanisms used here are quite out of date.
  • Matthew Sanford
    Matthew Sanford about 11 years
    1. What exactly do you think a timer does? 2. Adding to send 10 seconds after the data is requested is as simple as adding another EventWaitHAndle 3. This is the method recommended everywhere... 4. Lastly this is the best use of the resources... perhaps you should read the link I provided. Using a timer to do this is at best a very novice mistake.. but to each their own.
  • Servy
    Servy about 11 years
    1. If you don't reset it, and have AutoReset=true, the same thing. When it's not set to auto reset and is reset when an item is added, something quite different. 2. Then add it so that your code does what the question asks. 3. No, it's not. If you want proof, read your own link, it recommends a very different approach. 4. No, it's not the best use of resources, it burns a dedicated thread needlessly. Perhaps you should read your own link. It seems you're thinking about the first link you posted now marked "obsolete". That should give you a hint. How is using a timer a mistake?
  • Servy
    Servy about 11 years
    I find it kinda funny that you downvote my answer, despite the fact that it works, because I downvoted your answer because it doesn't work.
  • rednaks
    rednaks about 9 years
    Note that .Stop() will fire an Elapsed event. the best way is to remove the event handler and add it again : timer.Elapsed -= myEventHandler; timer.Stop(); timer.Elapsed += myEventHandler; timer.Start();
  • Servy
    Servy about 9 years
    @rednaks No, calling Stop doesn't fire the elapsed event. If you call Stop right around the same time as the event is about to fire, it's possible for there to be a race condition in which the event is fired after calling Stop because it wasn't called quickly enough to be able to stop the handler. There's a big difference there. A Timer, for all sorts of reasons, is only going to be an approximation; it's precision is limited, and that is a problem that is inherent to the Timer.