Stopping a System.Timers.Timer - c#

Let's say I have this code
public static Timer timer;
static void Main ()
{
timer = new Timer ( 60 * 1000 ); // It ticks every minute
timer.Elpased += One;
timer.Elapsed += Two;
}
private static void One ( sender o, EventArgs e )
{
timer.Stop ();
}
private static void Two ( sender o, EventArgs e )
{
DoSomething ();
}
Since I'm assuming that 'One' and 'Two' will execute in subscribe order, stopping the Timer in 'One' will prevent 'Two' from happening?
If not, how can I do it?

System.Threading.Timer does not have an Elapsed event. It requires a single TimerCallback delegate passed to the constructor, which it executes on a ThreadPool thread.
https://msdn.microsoft.com/en-us/library/system.threading.timer(v=vs.110).aspx
I believe you are looking at System.Timers.Timer, which does have an Elapsed event.
https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx
I do not believe there is a way to guarantee a way to prevent Two from firing the way you describe. You must assume that One and Two execute at the exact same time on 2 different threads. Even calling Stop() isn't guaranteed to prevent the timer from firing an additional time: https://msdn.microsoft.com/en-us/library/system.timers.timer.stop(v=vs.110).aspx.
My suggestion would be to have a single callback that handles the branching logic on if it should perform the actions in Two after the logic in One executes.

Related

Is there a way to use one Timer for multiple events?

I am in C# .NET 3.5
What happens when timer elapses and event handler is performed ?
Does the timer cease to exist ?
Can I register several events in different time on one timer, expecting them all to fire one after another ?
You can set a timer to fire off the event only once or continue to do it (Timer.AutoReset property). Yes, you can register several different event handlers on a single timer, but I don't know that there is any way of knowing what order they will fire. If that matters to you, set a single handler, and have that handler call the others. If what you are trying to do is to call a different handler, each time the timer goes off, I would suggest setting a single handler that keeps an enum indicating which function to call and incrementing it each time it gets called by the timer.
To call the same handler to "iterate" through a list of parameters, once on each interval elapsed, I would have an array or list of the parameters and the handler would just increase a counter or consume the list.
using System.Timers;
public class MyTimedDelete {
private static List<int> ListOfIds=null;
private static System.Timers.Timer myTimer=null;
public static void AddIdToQueue(int id)
{
if (ListOfIds == null)
{
ListOfIds = new List<int>();
myTimer = new System.Timers.Timer(2000);
myTimer.Elapsed += OnTimedEvent;
}
ListOfIds.Add(id);
if (ListOfIds.Count==1)
{
myTimer.Start();
}
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
deleteItem(ListOfIds[0]);
ListOfIds.RemoveAt(0);
if (ListOfIds.Count == 0) {
myTimer.Stop();
}
}
}

prevent this unsafe cross threading

I have this code:
private void Form1_Load(object sender, EventArgs e)
{
/*start update timer*/
System.Timers.Timer updateticker = new System.Timers.Timer();
updateticker.Elapsed += new ElapsedEventHandler(update_overload);
//10 minute ticker
updateticker.Interval = 600000;
//30 sec ticker
updateticker.Interval = 30000;
updateticker.Enabled = true;
System.Timers.Timer guiTimer = new System.Timers.Timer();
guiTimer.Elapsed += new ElapsedEventHandler(idle_display);
//1 minute ticker
guiTimer.Interval = 60000;
//30 sec ticker
//updateticker.Interval = 30000;
guiTimer.Enabled = true;
}
//run front end idle timer
public void idle_display(object source, ElapsedEventArgs e)
{
if (minutes_left > 0) {
minutes_left = minutes_left - 1;
}
lbl_dyn_status.Text = "Time until next automatic update: "+ minutes_left + " minutes.";
}
Visual studio is flagging the final line of the second function as unsafe cross threading. Can anyone suggest how I rewrite this to solve the issue?
Cheers
Use System.Windows.Forms.Timer instead of System.Timers.Timer
or use SynchronizingObject property of Timer.
When SynchronizingObject is null, the method that handles the Elapsed event is called on a thread from the system-thread pool. For more information on system-thread pools, see ThreadPool.
When the Elapsed event is handled by a visual Windows Forms component, such as a button, accessing the component through the system-thread pool might result in an exception or just might not work. Avoid this effect by setting SynchronizingObject to a Windows Forms component, which causes the method that handles the Elapsed event to be called on the same thread that the component was created on.
As others suggested, use System.Windows.Forms.Timer. Unlike System.Threading.Timer, which operates on Thread Pool threads, it guarantees you that the tick event is invoked on the UI thread.
The UI thread is the only thread allows to modify the UI. This is why you get an exception trying to write lbl_dyn_status.Text.
Use System.Windows.Forms.Timer timer instead of System.Timers.Timer.
Change the handler to
public void IdleDisplay(object source, ElapsedEventArgs e)
{
if (lbl_dyn_status.InvokeRequired)
{
this.Invoke(IdleDisplay)
}
else
{
if (minutes_left > 0)
{
minutes_left = minutes_left - 1;
}
lbl_dyn_status.Text = string.Format(
"Time until next automatic update: {0} minutes.",
minutes_left);
}
}
This way allows you to use the thread free System.Threading.Timer but checks for a cross-thread call in the handler. If detected the call is invoked on the main GUI thread, via the Form class.
This is described on MSDN in more detail here.
More generally, you should not use timers to count time like this. The more thread bound your timer is, the more it is likely to diverge from real elapsed time. You can use your timer to schedule an update of your clock but, you should calculate elapsed time since some fixed point rather than using an iterative counter.

Synchronizing Forms.Timer and Diagnostics.Stopwatch

I have a function (say foo())that will be called from time to time with a variable interval. When it is called, it checks the time and takes an action accordingly.
I have done this in the following way:
A Forms.Timer object invokes the function when required
A Diagnostics.Stopwatch object is used within the function for the purpose of determining the time and deciding what to do.
However I have the following problem: when foo() is called by Timer's callback, the ElapsedMilliseconds value of stopwatch object is usually lower than expected. For example, timer is set to 1000 so after 1000 ms foo() is called, but within foo() body ElapsedMilliseconds return 900 therefore foo behaves as if the elapsed time was 900 (although it should take the action A because 1000 ms actually elapsed, it does not)
How can I synchronize timer and stopwatch in such case that ElapsedMilliseconds have a consistent value with timer?
EDIT: Some Code
Some sample code to explain what is my problem:
//foo is the function that is called by timer's callback
public void foo()
{
//Let's see what time it is:
long currentTime = stopwatch.ElapsedMilliseconds();
Item = getCurrentItem(currentTime);
Item.Draw();
}
//this is the callback of timer
private void timer1_Tick(object sender, EventArgs e)
{
//set the timer for next time
timer1.Interval = Intervals[periodIdx++];
foo();
}
This is supposed to draw something else each time when an interval is completed, however since ElapsedMilliseconds return an earlier value than timer claims, although the interval is over, next item isn't drawn
You get the big difference because you start the timer somewhere within the 1/64 second interval. You'll get better results with this:
private void StartTimers() {
int tick = Environment.TickCount;
while (Environment.TickCount == tick) Thread.Sleep(0);
timer1.Enabled = true;
stopwatch.Start();
}
Where the while() loop improves the odds that the timer gets started at the start of a 1/64 timer tick. Just improves, no guarantees. And you can't do anything about the Tick event firing late, it entirely depends on the responsiveness of your UI thread. It is however always late. Don't use this code, write your code so you don't care that these timers are not in sync. You may have to reduce the timer's Interval to accomplish that, it isn't clear from the question.
You aren't going to have much success with this approach. You're not starting each timer at the exact same time and you're not checking them at the exact same time (there some passage of time between the Timer firing it's event and your code querying the Stopwatch).
Pick a single timer and base everything off of it if you want things in sync. For example, if you want to go with the Forms.Timer, in your event handler for it just increment a counter variable - that will tell you how many times your handler has been called and, effectively, how much time the Forms.Timer says has passed. Here's an example (I'll leave it to you to handle the case of the timer ticking long enough that the counter exceeds long.MaxValue)
public void foo()
{
Item = getCurrentItem(totalElapsed);
Item.Draw();
}
long totalElapsed = 0;
private void timer1_Tick(object sender, EventArgs e)
{
totalElapsed += timer1.Interval;
//set the timer for next time
timer1.Interval = Intervals[periodIdx++];
foo();
}

Single threaded timer

I wanted a timer with the following properties:
No matter how many times start is called, only one call back thread is ever running
The time spent in the call back function was ignored with regards to the interval. E.g if the interval is 100ms and the call back takes 4000ms to execute, the callback is called at 100ms, 4100ms etc.
I couldn't see anything available so wrote the following code. Is there a better way to do this?
/**
* Will ensure that only one thread is ever in the callback
*/
public class SingleThreadedTimer : Timer
{
protected static readonly object InstanceLock = new object();
//used to check whether timer has been disposed while in call back
protected bool running = false;
virtual new public void Start()
{
lock (InstanceLock)
{
this.AutoReset = false;
this.Elapsed -= new ElapsedEventHandler(SingleThreadedTimer_Elapsed);
this.Elapsed += new ElapsedEventHandler(SingleThreadedTimer_Elapsed);
this.running = true;
base.Start();
}
}
virtual public void SingleThreadedTimer_Elapsed(object sender, ElapsedEventArgs e)
{
lock (InstanceLock)
{
DoSomethingCool();
//check if stopped while we were waiting for the lock,
//we don't want to restart if this is the case..
if (running)
{
this.Start();
}
}
}
virtual new public void Stop()
{
lock (InstanceLock)
{
running = false;
base.Stop();
}
}
}
Here's a quick example I just knocked up;
using System.Threading;
//...
public class TimerExample
{
private System.Threading.Timer m_objTimer;
private bool m_blnStarted;
private readonly int m_intTickMs = 1000;
private object m_objLockObject = new object();
public TimerExample()
{
//Create your timer object, but don't start anything yet
m_objTimer = new System.Threading.Timer(callback, m_objTimer, Timeout.Infinite, Timeout.Infinite);
}
public void Start()
{
if (!m_blnStarted)
{
lock (m_objLockObject)
{
if (!m_blnStarted) //double check after lock to be thread safe
{
m_blnStarted = true;
//Make it start in 'm_intTickMs' milliseconds,
//but don't auto callback when it's done (Timeout.Infinite)
m_objTimer.Change(m_intTickMs, Timeout.Infinite);
}
}
}
}
public void Stop()
{
lock (m_objLockObject)
{
m_blnStarted = false;
}
}
private void callback(object state)
{
System.Diagnostics.Debug.WriteLine("callback invoked");
//TODO: your code here
Thread.Sleep(4000);
//When your code has finished running, wait 'm_intTickMs' milliseconds
//and call the callback method again,
//but don't auto callback (Timeout.Infinite)
m_objTimer.Change(m_intTickMs, Timeout.Infinite);
}
}
The .NET Framework provides four timers. Two of these are general-purpose multithreaded
timers:
System.Threading.Timer
System.Timers.Timer
The other two are special-purpose single-threaded timers:
System.Windows.Forms.Timer (Windows Forms timer)
System.Windows.Threading.DispatcherTimer (WPF timer)
The last 2 are designed to eliminate thread-safety issues for WPF and Windows Forms applications.
For example, using WebBrowser inside a timer to capture screenshots from webpage needs to be single-threaded and gives an error at runtime if it is on another thread.
The single-thread timers have the following benefits
You can forget about thread safety.
A fresh Tick will never fire until the previous Tick has finished
processing.
You can update user interface elements and controls directly from
Tick event handling code, without calling Control.BeginInvoke or
Dispatcher.BeginIn voke.
and main disadvantage to note
One thread serves all timers—as well as the processing UI events.
Which means that the Tick event handler must execute quickly,
otherwise the user interface becomes unresponsive.
source: most are scraps from C# in a Nutshell book -> Chapter 22 -> Advanced threading -> Timers -> Single-Threaded Timers
For anyone who needs a single thread timer and wants the timer start to tick after task done.
System.Timers.Timer could do the trick without locking or [ThreadStatic]
System.Timers.Timer tmr;
void InitTimer(){
tmr = new System.Timers.Timer();
tmr.Interval = 300;
tmr.AutoReset = false;
tmr.Elapsed += OnElapsed;
}
void OnElapsed( object sender, System.Timers.ElapsedEventArgs e )
{
backgroundWorking();
// let timer start ticking
tmr.Enabled = true;
}
Credit to Alan N
source https://www.codeproject.com/Answers/405715/System-Timers-Timer-single-threaded-usage#answer2
Edit: spacing
Look at the [ThreadStatic] attribute and the .Net 4.0 ThreadLocal generic type. This will probably quickly give you a way to code this without messing with thread locking etc.
You could have a stack inside your time class, and you could implement a Monitor() method that returns a IDisposable, so you can use the timer like so:
using (_threadTimer.Monitor())
{
// do stuff
}
Have the timer-monitor pop the the interval timestamp off the stack during Dispose().
Manually coding all the locking and thread recognition is an option as has been mentioned. However, locking will influence the time used, most likely more than having to initialize an instance per thread using ThreadLocal
If you're interested, I might knock up an example later
Here is a simple PeriodicNonOverlappingTimer class, that provides just the requested features, and nothing more than that. This timer cannot be started and stopped on demand, and neither can have its interval changed. It just invokes the specified action periodically in a non overlapping manner, until the timer is disposed.
/// <summary>
/// Invokes an action on the ThreadPool at specified intervals, ensuring
/// that the invocations will not overlap, until the timer is disposed.
/// </summary>
public class PeriodicNonOverlappingTimer : IDisposable, IAsyncDisposable
{
private readonly System.Threading.Timer _timer;
public PeriodicNonOverlappingTimer(Action periodicAction,
TimeSpan dueTime, TimeSpan period)
{
// Arguments validation omitted
_timer = new(_ =>
{
var stopwatch = Stopwatch.StartNew();
periodicAction();
var nextDueTime = period - stopwatch.Elapsed;
if (nextDueTime < TimeSpan.Zero) nextDueTime = TimeSpan.Zero;
try { _timer.Change(nextDueTime, Timeout.InfiniteTimeSpan); }
catch (ObjectDisposedException) { } // Ignore this exception
});
_timer.Change(dueTime, Timeout.InfiniteTimeSpan);
}
public void Dispose() => _timer.DisposeAsync().AsTask().Wait();
public ValueTask DisposeAsync() => _timer.DisposeAsync();
}
Usage example. Shows how to create a non-overlapping timer that starts immediately, with a period of 10 seconds.
var timer = new PeriodicNonOverlappingTimer(() =>
{
DoSomethingCool();
}, TimeSpan.Zero, TimeSpan.FromSeconds(10));
//...
timer.Dispose(); // Stop the timer once and for all
In case the DoSomethingCool fails, the exception will be thrown on the ThreadPool, causing the process to crash. So you may want to add a try/catch block, and handle all the exceptions that may occur.
The Dispose is a potentially blocking method. If the periodicAction is currently running, the Dispose will block until the last invocation is completed.
If you don't want to wait for this to happen, you can do this instead:
_ = timer.DisposeAsync(); // Stop the timer without waiting it to finish

How to raise only 1 Timer event in C#?

How do I get a timer event to fire one at a time.
For example I have a timer that raises an event every 10 minutes.
The event that is raised takes 10 or more minutes to finish executing.
I would like the timer to reset AFTER the event has finished.
In other words I do not want to raise more than 1 instance of the event at any one time.
Use System.Timers.Timer not the Threading one
Set AutoReset to false.
Then Start it again when you're done.
Usually what I do is have my event stop the timer when it's raised and then restart the timer when the event process completes:
private void timerHandler(object sender, TimerElapsedEventArgs e)
{
Timer timer = (Timer)sender;
timer.Stop();
RunProcess();
timer.Start();
}
public void RunProcess()
{
/* Do stuff that takes longer than my timer interval */
}
Now my timer will start again on completion of the process
It may be difficult to stop timers for efficiency or logic. The following code synchronizes skipping the events.
static readonly object key = new object();
void TimerHandler(object sender, TimerElapsedEventArgs e)
{
if(Monitor.TryEnter(key))
{
try
{
//do your stuff
}
finally
{
Montitor.Exit(key);
}
}
}

Categories

Resources