Both System.Timers.Timer
and System.Threading.Timer
fire at intervals that are considerable different from the requested ones.
For example:
new System.Timers.Timer(1000d / 20);
yields a timer that fires 16 times per second, not 20.
To be sure that there are no side-effects from too long event handlers, I wrote this little test program:
int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };
// Test System.Timers.Timer
foreach (int frequency in frequencies)
{
int count = 0;
// Initialize timer
System.Timers.Timer timer = new System.Timers.Timer(1000d / frequency);
timer.Elapsed += delegate { Interlocked.Increment(ref count); };
// Count for 10 seconds
DateTime start = DateTime.Now;
timer.Enabled = true;
while (DateTime.Now < start + TimeSpan.FromSeconds(10))
Thread.Sleep(10);
timer.Enabled = false;
// Calculate actual frequency
Console.WriteLine(
"Requested frequency: {0}\nActual frequency: {1}\n",
frequency, count / 10d);
}
The output looks like this:
Requested: 5 Hz; actual: 4,8 Hz
Requested: 10 Hz; actual: 9,1 Hz
Requested: 15 Hz; actual: 12,7 Hz
Requested: 20 Hz; actual: 16 Hz
Requested: 30 Hz; actual: 21,3 Hz
Requested: 50 Hz; actual: 31,8 Hz
Requested: 75 Hz; actual: 63,9 Hz
Requested: 100 Hz; actual: 63,8 Hz
Requested: 200 Hz; actual: 63,9 Hz
Requested: 500 Hz; actual: 63,9 Hz
The actual frequency deviates by up to 36% from the requested one. (And evidently cannot exceed 64 Hz.) Given that Microsoft recommends this timer for its "greater accuracy" over System.Windows.Forms.Timer
, this puzzles me.
Btw, these are not random deviations. They are the same values every time.
And a similar test program for the other timer class, System.Threading.Timer
, shows the exact same results.
In my actual program, I need to collect measurements at precisely 50 samples per second. This should not yet require a real-time system. And it is very frustrating to get 32 samples per second instead of 50.
Any ideas?
@Chris:
You are right, the intervals all seem to be integer multiples of something around 1/64th second. Btw, adding a Thread.Sleep(...) in the event handler doesn't make any difference. This makes sense given that System.Threading.Timer
uses the thread pool, so each event is fired on a free thread.
Meskipun C dibuat untuk memprogram sistem dan jaringan komputer namun bahasa ini juga sering digunakan dalam mengembangkan software aplikasi. C juga banyak dipakai oleh berbagai jenis platform sistem operasi dan arsitektur komputer, bahkan terdapat beberepa compiler yang sangat populer telah tersedia.
C adalah huruf ketiga dalam alfabet Latin. Dalam bahasa Indonesia, huruf ini disebut ce (dibaca [tʃe]).
Bahasa pemrograman C ini dikembangkan antara tahun 1969 – 1972 oleh Dennis Ritchie. Yang kemudian dipakai untuk menulis ulang sistem operasi UNIX. Selain untuk mengembangkan UNIX, bahasa C juga dirilis sebagai bahasa pemrograman umum.
If you use winmm.dll you can use more CPU time, but have better control.
Here is your example modified to use the winmm.dll timers
const String WINMM = "winmm.dll";
const String KERNEL32 = "kernel32.dll";
delegate void MMTimerProc (UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2);
[DllImport(WINMM)]
static extern uint timeSetEvent(
UInt32 uDelay,
UInt32 uResolution,
[MarshalAs(UnmanagedType.FunctionPtr)] MMTimerProc lpTimeProc,
UInt32 dwUser,
Int32 fuEvent
);
[DllImport(WINMM)]
static extern uint timeKillEvent(uint uTimerID);
// Library used for more accurate timing
[DllImport(KERNEL32)]
static extern bool QueryPerformanceCounter(out long PerformanceCount);
[DllImport(KERNEL32)]
static extern bool QueryPerformanceFrequency(out long Frequency);
static long CPUFrequency;
static int count;
static void Main(string[] args)
{
QueryPerformanceFrequency(out CPUFrequency);
int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };
foreach (int freq in frequencies)
{
count = 0;
long start = GetTimestamp();
// start timer
uint timerId = timeSetEvent((uint)(1000 / freq), 0, new MMTimerProc(TimerFunction), 0, 1);
// wait 10 seconds
while (DeltaMilliseconds(start, GetTimestamp()) < 10000)
{
Thread.Sleep(1);
}
// end timer
timeKillEvent(timerId);
Console.WriteLine("Requested frequency: {0}\nActual frequency: {1}\n", freq, count / 10);
}
Console.ReadLine();
}
static void TimerFunction(UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2)
{
Interlocked.Increment(ref count);
}
static public long DeltaMilliseconds(long earlyTimestamp, long lateTimestamp)
{
return (((lateTimestamp - earlyTimestamp) * 1000) / CPUFrequency);
}
static public long GetTimestamp()
{
long result;
QueryPerformanceCounter(out result);
return result;
}
And here is the output I get:
Requested frequency: 5
Actual frequency: 5
Requested frequency: 10
Actual frequency: 10
Requested frequency: 15
Actual frequency: 15
Requested frequency: 20
Actual frequency: 19
Requested frequency: 30
Actual frequency: 30
Requested frequency: 50
Actual frequency: 50
Requested frequency: 75
Actual frequency: 76
Requested frequency: 100
Actual frequency: 100
Requested frequency: 200
Actual frequency: 200
Requested frequency: 500
Actual frequency: 500
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With