Virtual Developer Workshop: Containerized Development with Docker
Environment: Timers, miscellaneous
This class supplies an accurate timer (figured in milliseconds). There are two advantages to this class instead of the normal Sleep():
- It can sleep the remaining time of a chosen period, causing exact timeframes
- It's more accurate
When you want something to execute every second, and your action takes 300 ms, you face the problem that with a Sleep(1000) after the action, the complete process takes more then 1300 milliseconds.
A further problem was the accuracy of a Sleep. This is not high. A Sleep of 300ms can take 310ms. a Sleep of 1ms takes at least 8ms. I did a check on different machines; it is always different and always bad.
To avoid this inaccuracy of Sleep, I only used Sleep() to approach the end time up to 15 milliseconds. In the last part, I used a dirty for() loop. Because this loop generates a big CPU usage, I only used this at the very end of the waiting time. Because of the small period, you cannot see any difference in the NT-performance meter.
When you don't need to use this level of accuracy, only the remain sleep function, InitTimer(false), will avoid this for() loop, to the disadvantage of the accuracy.
And How Does It Work Under Water?
The best way for a good timer to operate is to use the QueryPerformanceCounter(). It gives clockpulses. Because this is hardware-dependent, we need to know the frequency (how many pulses per second). This is achieved by using QueryPerformanceFrequency(). QueryPerformanceCounter() works with a struct LARGE_INTEGER, which has some disadvantages when calculating. It's no problem to use a LONGLONG (__int64) and cast it. This saves us from a lot of casting and time-consuming member accesses.
Because my app runs for long times, I looked to the maximum time it could run: We store in a __int64; its maximum val is 9.22337E+18 (=2^63). This is the maximum period we can sleep.
The frequency/divider of a P2-333 MHz is 1,193,180
The frequency/divider of a P4-2.4 GHz is 3,579,545
The worse case is P4. This means we can store a maximum of:
2,576,688,388,288 seconds 42,944,806,471 minutes 715,746,775 hours 29,822,782 days 81,706 years
This is enough for my humble program...
Internal variables are all saved as clockpulses, not in milliseconds. All user input/output is done in milliseconds and translated to/from clockpulses.
The function SleepNow() acts just as an ordinary Sleep() but when you name it the same, there is an unwanted case of recursive programming when you just need ten original Sleep() cycles.
When sleeping, it acts the same as a normal sleep: You cannot interact with the app anymore. You can put it in a separate thread to avoid this. In the test app, I didn't do this. It's just a simple test container about the use of the timer class. In your own app, it's enough to include CKETimer.cpp and CKETimer.h.
Yes, I left some ends open, so you guys (girls?) out there can extend it with your ideas:
- I don't like the for loop, but I can't think of a process that is short (<1ms) and doesn't boos CPU usage.
- Make it thread safe
- Test it on other OS/PC's (it works on Win98, 2000 and XP, all >= P2)