Reset System.Timers.Timer to prevent Elapsed event
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).
tedski
Updated on May 16, 2020Comments
-
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 aList<string>
I'd like to send. I want the following to happen:- Add string to
List
- Start
Timer
for 10 seconds - Add second string to
List
beforeTimer.Elapsed
- 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?
- Add string to
-
Andrew Savinykh about 11 yearsYour link shows "This topic is obsolete." and does not contain useful information.
-
Andrew Savinykh about 11 yearsFrom 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 about 11 years@zespri That's exactly how I read it to. That is exactly what this code does.
-
Matthew Sanford about 11 yearseven 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 about 11 yearsThe only problem is when the
Start()
function is called a second time, it doesn't delay theElapsed
event. -
tedski about 11 yearsI have no idea what you are talking about or what that page is supposed to be telling me
-
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 about 11 yearsFrom 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 about 11 years@tedski Sorry, that functionality applies to other types of
Timer
classes. For this class you just need to callStop
and then start, which is still a simple enough change. See edit. -
tedski about 11 yearsAwesome, that answered my question. I am going to have to look into what @MatthewSanford was talking about now.
-
tedski about 11 yearsThanks, this looks more digestible. I accepted the other answer for now, I have a meeting but will look over your response after.
-
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 about 11 yearsThis 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 about 11 years1. 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 about 11 years1. 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 about 11 yearsI 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 about 9 yearsNote that
.Stop()
will fire anElapsed
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 about 9 years@rednaks No, calling
Stop
doesn't fire the elapsed event. If you callStop
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 callingStop
because it wasn't called quickly enough to be able to stop the handler. There's a big difference there. ATimer
, 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 theTimer
.