2 hours ago, Hodgman said:IMHO, the primary purpose of OS threads is to allow you to run code on more that one CPU core, which is something that you want to do when you've got code that's extremely computationally intensive.
Waiting for a certain amount of time to elapse is about the least computationally intensive thing you can do, so you shouldn't use threads to solve that problem.
Games are also a little different to typical software, in that we typically have a "game loop" that's running at real-time rates, e.g. 30Hz. The most typical solution for timers in games is to simply poll them once per frame. i.e. for each timer, in the main game loop (once per frame), check if the timer has elapsed and run the associated event if so.
This is true on a small number of buggy AMD CPUs from the early 2000's, if they don't have the appropriate patches installed, if they use certain power saving settings, if you're still running on Windows XP or Windows Server 2003.
It's pretty safe to assume that QPC just works on modern Windows. These days, it will automatically select a different implementation that just works, depending on your hardware.
[edit] actually after posting, i re-researched this and found that Intel shipped some CPUs with this same bug as recently as 2014 and Win10 was observed actually selecting the buggy implementation...
I wish it was a problem that was confined to a handful of old processors that you rarely see in the wild anymore. On some systems the problem is in the BIOS and keep in mind that the vast majority of users never update their BIOS. The problem is made worse by the fact that most mainboard and system vendors do not provide an automated update path for it.
Implementing a timer using the method I suggested (on Windows) not only ensures an accurate timer across a broad range of systems, it also minimises scalability problems down the track. If your application/game has multiple threads on multiple cores and needs to call QueryPerformance from those threads, the resulting performance hit from doing so compared to calling it from a single dedicated thread is too high to ignore.
The so-called cost and complexity of threads, aside from being negligible in this context, is something that needs to be confronted and accepted in the era of many-core processors. In this case, the small investment of time and effort goes a long way. ?
Edit/Note: For C#/.Net Framework, call Stopwatch.GetTimestamp. It calls QueryPerformanceCounter internally. Do NOT use DateTime.Now or Environment.TickCount.