我有一个方法,我想每秒运行几千次。我目前使用DispatcherTimer以大约每毫秒一次的速度运行它,并将Interval属性设置为1ms。
现在,DispatcherTimer不是可用的最快、最高精度的计时器,而且由于Tick事件处理程序在UI线程上运行,因此它不会执行得比这更频繁。我调用的代码不需要在UI线程上运行,所以我似乎是在无缘无故地限制自己。
如果我想要更高的频率,我有什么选择?我应该使用System.Threading.Timer吗?是否有专门的计时器用于高频、高精度的操作?我是不是应该完全放弃计时器,去研究别的东西呢?
请注意,虽然我尝试(但由于缺乏声誉)将其标记为WindowsIot (因为我需要它在那里运行,并且可能新的设备库有专门针对此的东西),但这也适用于一般的.net应用程序,比如WPF、WinForms、WinRT等……
发布于 2015-05-12 14:54:21
潜在的问题是,当您获得CPU时间时,您是在OS调度系统中这样做的。这意味着像Thread.Sleep或实际上等待系统定时器的回调的操作受到系统定时器的频率的限制。这通常是15.6ms --比你的1ms要长得多。WPF绕过这一点的方法是,当动画进行时,它将改变全局计时器频率。最小值是0.5ms,这对你来说可能已经足够了--只要剩下的代码可以在0.5ms内执行(总共1ms的“帧时间”)。显然,要有一个可靠的频率有点棘手。
所以这可以归结为为什么你试图每秒运行1000次,以及你在这个周期中做了什么。
而且你几乎可以保证不会得到这样的分辨率。即使您将全局计时器设置为0.5ms,并创建了一个每秒执行一次的Timer,您仍然会与试图执行的其他线程发生冲突。
游戏中使用的方法之一是使用Stopwatch内部使用的高分辨率Timer - QueryPerformanceCounter。然而,这并不允许你让步-你必须主动旋转(除非你可以Thread.Sleep足够长的时间来允许你使用它-见上文)。请注意,大多数使用固定步长计时的游戏-就像你的场景一样,通常被限制在大约60 FPS -几乎每个周期17ms。这与每周期1ms相去甚远。当不使用固定步长计时时,你实际上可以做很多很酷的东西,但你必须考虑时间步长的实际长度-不完全简单,并且可能不适用于你的情况。
发布于 2015-05-12 14:54:13
您可以尝试使用无限循环,并使用Stopwatch自己进行计时。这使用本机rdtsc方法,该方法计算实际的CPU周期。它比.NET中的其他计时器精确得多,你可以避免秒表的开销,直接调用本机方法directly。如果调用本机方法,您可能希望使用QueryPerformanceCounter。
发布于 2015-05-12 15:18:13
我写了一些代码。请记住,这不是最好的解决方案,但它是有效的。
FrequencyRunner类
public class FrequencyRunner
{
private int _frequency;
private Action _action;
private bool _isStarted = false;
/// <summary>
/// частота
/// </summary>
public int Frequency { get { return _frequency; } set { _frequency = value; } }
/// <summary>
/// задача
/// </summary>
public Action Action { get { return _action; } set { _action = value; } }
/// <summary>
/// конструктор
/// </summary>
public FrequencyRunner()
{
_frequency = 1000;
_action = () => { };
}
/// <summary>
/// конструктор
/// </summary>
/// <param name="frequency"> частота </param>
/// <param name="action"> задача </param>
public FrequencyRunner(int frequency, Action action)
{
_frequency = frequency;
_action = action;
}
/// <summary>
/// запустить процесс
/// </summary>
public void Start()
{
_isStarted = true;
Task.Run(() =>
{
Stopwatch sw = new Stopwatch();
double interval = 1000 / _frequency;
while (_isStarted)
{
sw.Restart();
_action();
sw.Stop();
double timeLeft = sw.ElapsedMilliseconds;
if (timeLeft < interval)
{
// need wait some time...
sw.Restart();
timeLeft = interval - timeLeft;
while (sw.ElapsedMilliseconds < timeLeft)
{
// nothing to do
}
sw.Stop();
}
}
});
}
/// <summary>
/// завершить процесс
/// </summary>
public void Stop()
{
_isStarted = false;
}
}用法:
// run it for 2 seconds (25 times per second)
int i = 0;
var runner = new FrequencyRunner(25, () => Console.WriteLine("Counter: {0}", ++i));
runner.Start();
Thread.Sleep(2000);
runner.Stop();https://stackoverflow.com/questions/30183592
复制相似问题