以尽可能最快的间隔调用方法(定时器)

时间:2015-05-12 06:41:33

标签: c# .net timer

我有一种方法,我想每秒运行数千次。我目前使用DispatcherTimer以大约每毫秒一次的速度运行它,Interval属性设置为1ms。

现在,DispatcherTimer不是最快,最高精度的定时器,而且由于Tick事件处理程序在UI线程上运行,因此它不会比这更频繁地执行。我正在调用的代码不需要在UI线程上运行,所以我似乎在限制自己没有真正的理由。

如果我想要更高的频率,我有哪些选择?我应该使用System.Threading.Timer吗?是否有特定的定时器用于高频,高精度操作?我应该完全放弃计时器并调查其他内容吗?

请注意,虽然我尝试(但由于缺乏声誉而失败)将其标记为WindowsIot(因为我需要它在那里运行,并且可能新的Devices库具有专门用于此的东西),这适用于.net应用程序一般来说,我是WPF,WinForms,WinRT等......

4 个答案:

答案 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 - 所以你必须使用不同的语言。