¿Por qué los temporizadores .NET están limitados a una resolución de 15 ms?

Tenga en cuenta que estoy preguntando sobre algo que llamará a una función de devolución de llamada más de una vez cada 15 ms usando algo comoSystem.Threading.Timer. No estoy preguntando cómo cronometrar con precisión un código utilizando algo comoSystem.Diagnostics.Stopwatch o inclusoQueryPerformanceCounter.

Además, he leído las preguntas relacionadas:

Temporizador preciso de Windows? System.Timers.Timer () está limitado a 15 ms

Temporizador de alta resolución en .NET

Ninguno de los cuales proporciona una respuesta útil a mi pregunta.

Además, el artículo recomendado de MSDN,Implemente un proveedor de tiempo de alta resolución y actualización continua para Windows, se trata de cronometrar cosas en lugar de proporcionar un flujo continuo de ticks.

Con eso dicho. . .

Hay mucha información incorrecta sobre los objetos de temporizador .NET. Por ejemplo,System.Timers.Timer se anuncia como "un temporizador de alto rendimiento optimizado para aplicaciones de servidor". YSystem.Threading.Timer es considerado de alguna manera un ciudadano de segunda clase. La sabiduría convencional es queSystem.Threading.Timer es una envoltura alrededor de WindowsTemporizadores de cola de temporizador y esoSystem.Timers.Timer Es algo completamente diferente.

La realidad es muy diferente.System.Timers.Timer es solo una envoltura delgada de componentesSystem.Threading.Timer (solo use Reflector o ILDASM para mirar adentroSystem.Timers.Timer y verás la referencia aSystem.Threading.Timer), y tiene un código que proporcionará sincronización automática de subprocesos para que no tenga que hacerlo.

System.Threading.Timer, como resultano es un contenedor para los temporizadores de cola de temporizador. Al menos no en el tiempo de ejecución 2.0, que se utilizó desde .NET 2.0 hasta .NET 3.5. Unos minutos con la CLI de fuente compartida muestra que el tiempo de ejecución implementa su propia cola de temporizador que es similar a los temporizadores de cola de temporizador, pero nunca llama a las funciones de Win32.

Parece que el tiempo de ejecución de .NET 4.0 también implementa su propia cola de temporizador. Mi programa de prueba (ver más abajo) proporciona resultados similares en .NET 4.0 como lo hace en .NET 3.5. Creé mi propio contenedor administrado para los temporizadores de cola de temporizador y probé que puedo obtener una resolución de 1 ms (con bastante buena precisión), por lo que considero poco probable que esté leyendo mal la fuente de CLI.

Tengo dos preguntas:

Primero, ¿qué causa que la implementación del tiempo de ejecución de la cola del temporizador sea tan lenta? No puedo obtener una resolución superior a 15 ms, y la precisión parece estar en el rango de -1 a +30 ms. Es decir, si solicito 24 ms, obtendré ticks entre 23 y 54 ms de diferencia. Supongo que podría pasar más tiempo con la fuente CLI para localizar la respuesta, pero pensé que alguien aquí podría saberlo.

Segundo, y me doy cuenta de que esto es más difícil de responder, ¿por qué no usar los temporizadores de cola de temporizador? Me doy cuenta de que .NET 1.x tuvo que ejecutarse en Win9x, que no tenía esas API, pero han existido desde Windows 2000, que si no recuerdo mal, era el requisito mínimo para .NET 2.0. ¿Es porque la CLI tuvo que ejecutarse en cajas que no son de Windows?

Mi programa de prueba de temporizadores:

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();
        }
    }
}