Почему таймеры .NET ограничены разрешением 15 мс?

Обратите внимание, что я спрашиваю о том, что будет вызывать функцию обратного вызова чаще, чем раз в 15 мс, используя что-то вроде System.Threading.Timer . Я не спрашиваю, как точно рассчитать время фрагмента кода, используя что-то вроде System.Diagnostics.Stopwatch или даже QueryPerformanceCounter .

Кроме того, я читал связанные вопросы :

Точный таймер Windows? System.Timers. Timer () ограничен 15 мс

Таймер высокого разрешения в .NET

Ни один из них не дает полезного ответа на мой вопрос.

Кроме того, рекомендованная статья MSDN Реализовать непрерывное обновление, Поставщик времени высокого разрешения для Windows предназначен для синхронизации, а не для обеспечения непрерывного потока тиков.

С учетом сказанного. . .

Есть много плохой информации об объектах таймера .NET. Например, System.Timers.Timer объявлен как «высокопроизводительный таймер, оптимизированный для серверных приложений». А System.Threading.Timer почему-то считается гражданином второго сорта. Принято считать, что System.Threading. Timer - это оболочка для Windows Timer Queue Timers , а System.Timers.Timer - это совсем другое.

В действительности все обстоит иначе. System.Timers.Timer - это просто тонкая оболочка компонента вокруг System.Threading.Timer (просто используйте Reflector или ILDASM, чтобы заглянуть внутрь System.Timers.Timer и вы увидите ссылку на System.Threading.Timer ), а также код, который обеспечит автоматическую синхронизацию потоков, так что вам не придется этого делать.

System.Threading.Timer , как оказалось, не является оболочкой для таймеров очереди таймера. По крайней мере, не в среде выполнения 2.0, которая использовалась от .NET 2.0 до .NET 3.5. Несколько минут с CLI общего источника показывают, что среда выполнения реализует свою собственную очередь таймера, аналогичную таймерам очереди таймера, но никогда не вызывает функции Win32.

Похоже, среда выполнения .NET 4.0 также реализует свой собственный таймер очередь. Моя тестовая программа (см. Ниже) дает такие же результаты в .NET 4.0, как и в .NET 3.5. Я создал свою собственную управляемую оболочку для таймеров очереди таймеров и доказал, что могу получить разрешение 1 мс (с довольно хорошей точностью), поэтому считаю маловероятным, что я неправильно читаю исходный код CLI.

У меня их два. вопросы:

Во-первых, что вызывает такую ​​медленную реализацию очереди таймера во время выполнения? Я не могу получить разрешение лучше, чем 15 мс, а точность находится в диапазоне от -1 до +30 мс. То есть, если я прошу 24 мс, я Я получу тики с интервалом от 23 до 54 мс. Я полагаю, что мог бы потратить еще немного времени на источник CLI, чтобы найти ответ, но подумал, что кто-то здесь может знать.

Во-вторых, и я понимаю, что на это труднее ответить, почему бы не использовать таймеры очереди? Я понимаю, что .NET 1.x должен был работать на Win9x, в котором не было этих API, но они существовали с Windows 2000, что, если я правильно помню, было минимальным требованием для .NET 2.0. Это потому, что интерфейс командной строки должен был работать на компьютерах, отличных от Windows?

Моя программа тестирования таймеров:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace TimerTest
{
    class Program
    {
        const int TickFrequency = 5;
        const int TestDuration = 15000;   // 15 seconds

        static void Main(string[] args)
        {
            // Create a list to hold the tick times
            // The list is pre-allocated to prevent list resizing
            // from slowing down the test.
            List tickTimes = new List(2 * TestDuration / TickFrequency);

            // Start a stopwatch so we can keep track of how long this takes.
            Stopwatch Elapsed = Stopwatch.StartNew();

            // Create a timer that saves the elapsed time at each tick
            Timer ticker = new Timer((s) =>
                {
                    tickTimes.Add(Elapsed.ElapsedMilliseconds);
                }, null, 0, TickFrequency);

            // Wait for the test to complete
            Thread.Sleep(TestDuration);

            // Destroy the timer and stop the stopwatch
            ticker.Dispose();
            Elapsed.Stop();

            // Now let's analyze the results
            Console.WriteLine("{0:N0} ticks in {1:N0} milliseconds", tickTimes.Count, Elapsed.ElapsedMilliseconds);
            Console.WriteLine("Average tick frequency = {0:N2} ms", (double)Elapsed.ElapsedMilliseconds / tickTimes.Count);

            // Compute min and max deviation from requested frequency
            double minDiff = double.MaxValue;
            double maxDiff = double.MinValue;
            for (int i = 1; i < tickTimes.Count; ++i)
            {
                double diff = (tickTimes[i] - tickTimes[i - 1]) - TickFrequency;
                minDiff = Math.Min(diff, minDiff);
                maxDiff = Math.Max(diff, maxDiff);
            }

            Console.WriteLine("min diff = {0:N4} ms", minDiff);
            Console.WriteLine("max diff = {0:N4} ms", maxDiff);

            Console.WriteLine("Test complete.  Press Enter.");
            Console.ReadLine();
        }
    }
}

61
задан Community 23 May 2017 в 12:10
поделиться