Windows forms中自带的Timer控件据说精度只有55ms。又有传说在Winnt 4.0以上精度可以达到10ms。
不知道为什么已经没有心情再去考证到底是怎么回事,决定自己写一个:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace Model
{
/// <summary>
/// 实际上这是一个Timer,此Timer通过另外一个线程实现,定期向主线程投递消息
/// </summary>
public class GameLoop
{
private readonly int _threadSleep;
private readonly int _notifierInterval;
private readonly ILoopProcessor _processor;
private Thread _timerThread;
private long _frameCount;
private Stopwatch _totalWatch = new Stopwatch();
public GameLoop(AvailableRefreshRates desiredRefreshRate, ILoopProcessor processor)
{
_processor = processor;
if (desiredRefreshRate == AvailableRefreshRates.FPSIs40)
{
_threadSleep = 10; //这个数据是实验得来的在性能和精度上比较平衡的数据
_notifierInterval = 20; //实际上肯定不会正好是这个数,而是偏大
}
else if (desiredRefreshRate == AvailableRefreshRates.FPSIs90)
{
_threadSleep = 10;
_notifierInterval = 10;
}
else
{
throw new NotSupportedException();
}
}
public void Start()
{
_timerThread = new Thread(new ThreadStart(this.Loop));
_timerThread.IsBackground = true;
_timerThread.Priority = ThreadPriority.BelowNormal;
_timerThread.Start();
}
public void Loop()
{
try
{
_totalWatch.Start();
Stopwatch watch = new System.Diagnostics.Stopwatch();
double msElapsed = 0;
watch.Start();
while (true)
{
System.Threading.Thread.Sleep(_threadSleep);
watch.Stop();
msElapsed = watch.Elapsed.TotalMilliseconds;
if (msElapsed > _notifierInterval)
{
watch.Reset();
watch.Start();
_frameCount++;
_totalWatch.Stop();
double totalElapsed = _totalWatch.Elapsed.TotalMilliseconds;
_totalWatch.Start();
bool quit = _processor.Process(msElapsed / 1000, _frameCount / (totalElapsed / 1000));
if (quit)
{
watch.Stop();
break;
}
}
else
{
watch.Start();
}
}
}
catch (Exception ex)
{
}
}
public enum AvailableRefreshRates
{
FPSIs40, FPSIs90
}
}
public interface ILoopProcessor
{
bool Process(double secondsElapsed, double rate);
}
}
效果:
可以看到刷新率可以达到90桢每秒。
不过我遇到了一个很奇怪的问题,关于Thread.Sleep。当指定的间隔小于10000tick的时候,cpu占用就接近100%,但是只要达到了10000tick,cpu占用立刻就变成0%。不知道是为什么。可能是因为当小于10000tick的时候是用自旋锁实现,大于等于10000tick的时候是用另外的方式实现的。
另外一个十分棘手的问题是屏幕闪烁。在Form上指定的ControlStyle后,仍然有一定的闪烁,难道GDI+的性能就这么成问题?
完整的代码:/Files/zhy2002/GDIPlusMovable.rar