我有一种方法,我想每秒运行数千次。我目前使用DispatcherTimer以大约每毫秒一次的速度运行它,Interval属性设置为1ms。
现在,DispatcherTimer不是最快,最高精度的定时器,而且由于Tick事件处理程序在UI线程上运行,因此它不会比这更频繁地执行。我正在调用的代码不需要在UI线程上运行,所以我似乎在限制自己没有真正的理由。
如果我想要更高的频率,我有哪些选择?我应该使用System.Threading.Timer吗?是否有特定的定时器用于高频,高精度操作?我应该完全放弃计时器并调查其他内容吗?
请注意,虽然我尝试(但由于缺乏声誉而失败)将其标记为WindowsIot(因为我需要它在那里运行,并且可能新的Devices库具有专门用于此的东西),这适用于.net应用程序一般来说,我是WPF,WinForms,WinRT等......
答案 0 :(得分:4)
潜在的问题是当你产生CPU时间时,你在OS调度系统中这样做了。这意味着像Thread.Sleep
这样的操作,或者实际上等待系统定时器的回调,受到系统定时器频率的限制。那通常是15.6ms - 远远超过你的1ms。 WPF绕过它的方式,允许平滑的动画等,是在动画正在进行时,它将改变全局计时器频率。最小值为0.5ms,可能足够好 - 只要其余代码可以在0.5ms内执行(总共1ms“帧时间”)。显然,拥有可靠的频率有点棘手。
所以这归结为为什么你试图每秒运行1000次,以及你在循环中正在做什么。
你几乎可以保证不来获得这种解决方案。即使您将全局计时器设置为0.5毫秒,并使每秒执行一个Timer
,您仍然会与其他尝试执行的线程作斗争。
游戏中使用的一种方法是使用QueryPerformanceCounter
内部使用的高分辨率计时器Stopwatch
。但是,这不允许你屈服 - 你必须主动旋转(除非你能Thread.Sleep
足够长的时间允许你使用它 - 见上文)。请注意,大多数使用固定步进计时的游戏 - 类似于您的场景通常限制在大约60 FPS - 每个周期几乎17毫秒。这与每个周期1ms相差甚远。当不使用固定步长时,你实际上可以制作很多很酷的东西,但你必须考虑时间步长的实际长度 - 不是很简单,可能不适用于你的情况。
答案 1 :(得分:2)
您可以尝试使用无限循环,并使用item_t* root
自行完成计时。这使用本机rdtsc方法,该方法计算实际的CPU周期。它比.NET中的其他计时器准确得多。您可以避免使用Stopwatch的开销并调用本机方法directly。如果调用本机方法,您可能希望使用QueryPerformanceCounter。
答案 2 :(得分:1)
我写了一些代码。请记住,它不是最好的解决方案,但它确实有效。
班级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();
答案 3 :(得分:0)
正如Luaan所提到的,如果你使用Sleep,你就会受操作系统的支配。
如果按照精确的间隔调用对您很重要,那么SpinWait和SpinUntil就是您想要的。请注意,这将继续执行代码,直到满足条件/定时器,因此您的线程将占用您正在执行的核心上100%的CPU时间。 (如果您使用的是4核计算机,则会看到25%的CPU使用率)。
这并不能保证您将在您想要的时间执行 - 操作系统仍然可以自由地窃取CPU时间并安排其他线程执行。保证在精确间隔内执行的唯一方法是使用类似VxWorks的Realtime OS。这有保证确保指定的cpu时间分配。当然,RTOS不会运行.NET - 所以你必须使用不同的语言。