Почему таймеры .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Таймер очереди Таймеры и это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 Shared Source показывают, что среда выполнения реализует свою собственную очередь таймера, которая похожа на таймеры очереди таймера, но фактически никогда не вызывает функции 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. Это потому, что CLI должен был работать на компьютерах, отличных от 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<double> tickTimes = new List<double>(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();
        }
    }
}

Ответы на вопрос(3)

Ваш ответ на вопрос