I was originally using:
System.Windows. Forms.Timer
It started to lock my Window Service up, so I went to the next
evolution:
System.Threadin g.Timer
All was good. I was using it to send a message once a minute (to
another application on our network), logging to a text file each time
the message was sent (with a date / time stamp).
Then I noticed.. the time stamps in the log file were spacing out.
They would be at 1 minute intervals for a while, and then over time
they would gradually be something like 1 minute and 20 seconds.. 1
minute 27 seconds, etc. up until they were like several minutes apart.
I was confused at this point because I was under the impression that a
System.Threadin g.Timer ran in it's own environment and was independent
of anything else.
So I've been doing some research, at the simplist form I could conjure
up. I am now testing with:
using System.Timers;
Attempting to use 1 second intervals for testing purposes, the
resulting output is as follows:
09/24/2004 10:09:29.9687
09/24/2004 10:09:30.9687
09/24/2004 10:09:31.9687
09/24/2004 10:09:32.9687
09/24/2004 10:09:33.9687
09/24/2004 10:09:34.9687
09/24/2004 10:09:35.9687
09/24/2004 10:09:36.9687
09/24/2004 10:09:37.9843
09/24/2004 10:09:38.9843
09/24/2004 10:09:39.9843
09/24/2004 10:09:40.9843
09/24/2004 10:09:41.9843
09/24/2004 10:09:42.9843
09/24/2004 10:09:43.9843 <-- Notice Jump from 43 seconds to 45 on
next line
09/24/2004 10:09:45.0000
09/24/2004 10:09:46.0000
09/24/2004 10:09:47.0000
09/24/2004 10:09:48.0000
09/24/2004 10:09:49.0000
09/24/2004 10:09:50.0000
09/24/2004 10:09:51.0000
09/24/2004 10:09:52.0000
09/24/2004 10:09:53.0156
09/24/2004 10:09:54.0156
09/24/2004 10:09:55.0156
09/24/2004 10:09:56.0156
09/24/2004 10:09:57.0156
09/24/2004 10:09:58.0156
09/24/2004 10:09:59.0156
10/24/2004 10:10:00.0312
The following is the "snapTimer" DLL (A Visual Studio 2003 C# project)
that I use in this test:
using System;
using System.Diagnost ics;
using System.Timers;
namespace snapTimer
{
/// <summary>
/// Summary description for Timer.
/// </summary>
// A delegate type for hooking up change notifications.
public delegate void ElapsedEventHan dler(object sender, EventData e);
public class PlugIn : IDisposable
{
private static int _IntervalInSeco nds = 0;
private bool _Enabled = false;
private System.Timers.T imer watchDogTimer;
public event ElapsedEventHan dler Elapsed;
public PlugIn(int IntervalInSecon ds)
{
_IntervalInSeco nds = IntervalInSecon ds;
}
// Invoke the Changed event; called whenever list changes
protected virtual void OnElapsed(Event Data e)
{
if (Elapsed != null)
Elapsed(this, e);
}
private void EnableTimer()
{
watchDogTimer = new System.Timers.T imer(_IntervalI nSeconds * 1000);
watchDogTimer.A utoReset = true;
watchDogTimer.E lapsed -=new
System.Timers.E lapsedEventHand ler(watchDogTim er_Elapsed);
watchDogTimer.E lapsed +=new
System.Timers.E lapsedEventHand ler(watchDogTim er_Elapsed);
watchDogTimer.E nabled = true;
}
private void DisableTimer()
{
lock(this)
{
try
{
watchDogTimer.D ispose();
}
catch (Exception e1)
{
EventLog.WriteE ntry("snapTimer ", "Enabled = False\r\nError: " +
e1.ToString(), EventLogEntryTy pe.Error);
}
finally
{
watchDogTimer = null;
}
} // lock(this)
}
public bool Enabled
{
get { return _Enabled; }
set
{
_Enabled = value;
if (_Enabled) { EnableTimer(); }
else DisableTimer();
}
}
#region IDisposable Members
public void Dispose()
{
_Enabled = false; DisableTimer();
}
#endregion
private void watchDogTimer_E lapsed(object sender, ElapsedEventArg s
e)
{
OnElapsed(new EventData());
}
}
}
//
// EVENTDATA.CS - START
//
using System;
namespace snapTimer
{
/// <summary>
/// Summary description for EventData.
/// </summary>
public class EventData : EventArgs
{
private int SelectedRecID = 0;
public int RetID()
{
return SelectedRecID;
}
}
}
//
// EVENTDATA.CS - FINISH
//
The Windows Application I use for testing this, the following is the
segment of Code that deals with this Timer:
//
//
// START OF TEST SAMPLE CODE
//
//
private void cmdStart_Click( object sender, System.EventArg s e)
{
StartTimer();
}
private void StartTimer()
{
try
{
oTimer = new snapTimer.PlugI n(1);
oTimer.Elapsed -= new ElapsedEventHan dler(oTimer_Ela psed);
oTimer.Elapsed += new ElapsedEventHan dler(oTimer_Ela psed);
oTimer.Enabled = true;
}
catch (Exception e1)
{
MessageBox.Show ("Error: " + e1.ToString());
oTimer = null;
}
}
private void StopTimer()
{
try
{
if (oTimer != null)
{
oTimer.Elapsed -= new ElapsedEventHan dler(oTimer_Ela psed);
oTimer.Dispose( );
}
}
catch (Exception e1)
{
MessageBox.Show ("Error: " + e1.ToString());
}
finally
{
oTimer = null;
// Force garbage collection.
GC.Collect(GC.M axGeneration);
GC.WaitForPendi ngFinalizers();
}
}
private void cmdStop_Click(o bject sender, System.EventArg s e)
{
StopTimer();
}
private void oTimer_Elapsed( object sender, EventData e)
{
txtReport.Text +=
DateTime.Now.To String("mm/dd/yyyy hh:mm:ss.ffff") + "\r\n";
StopTimer();
StartTimer();
}
private void Form1_Closing(o bject sender,
System.Componen tModel.CancelEv entArgs e)
{
StopTimer();
}
//
//
// END OF TEST SAMPLE CODE
//
//
The Test Application is nothing more than 2 command buttons and a
textbox for dumping the Timestamp results to.
When the Timer is started, it hooks into an EventHandler that surfaces
an Event everytime the Timer elapses.
The Garbage Collection was an attempt to help clean up resources
during real time, because it appears every time I add an event handler
it just continues to leak memory. Even though when I terminate the
timer, I release the event handler the way I thought you were suppose
to.
I busted the "Timer" events into a separate DLL incase I needed to
tweak the settings for any reason.
Any feedback on how to better approach or handle this?