Notez que je parle de quelque chose qui va appeler une fonction de rappel plus souvent qu'une fois tous les 15 ms en utilisant quelque chose comme System.Threading.Timer
. Je ne suis pas demandant comment avec précision le temps d'un morceau de code à l'aide de quelque chose comme System.Diagnostics.Stopwatch
ou même QueryPerformanceCounter
.
Aussi, j'ai lu les questions connexes:
http://stackoverflow.com/questions/163022/high-resolution-timer-in-net
Ni de qui fournit une réponse utile à ma question.
En outre, il est recommandé d'article MSDN, de mettre en Œuvre une Permanence de la mise à Jour, de Temps à Haute Résolution Fournisseur pour Windows, est une question de timing des choses plutôt que de fournir un flux continu de tiques.
Avec qui dit. . .
Il ya tout un tas de mauvaises informations là-bas sur le .NET minuterie objets. Par exemple, System.Timers.Timer
est présenté comme "une haute performance de la minuterie optimisé pour les applications serveur." Et System.Threading.Timer
est en quelque sorte considéré comme un citoyen de deuxième classe. La sagesse conventionnelle est que l' System.Threading.Timer
est un wrapper autour des Fenêtres de la Minuterie de la File d'attente des Minuteries et qu' System.Timers.Timer
c'est autre chose.
La réalité est très différente. System.Timers.Timer
est juste une mince composant wrapper autour de System.Threading.Timer
(il suffit d'utiliser un Réflecteur ou ILDASM à coup d'oeil à l'intérieur d' System.Timers.Timer
et vous verrez la référence à l' System.Threading.Timer
), et a peu de code qui permettra de fournir du fil automatique de la synchronisation de sorte que vous n'avez pas à le faire.
System.Threading.Timer
, comme il s'avère , n'est pas un wrapper pour le compte à rebours de la File d'attente des Chronomètres. Au moins pas dans le runtime 2.0, qui a été utilisé .NET 2.0 par le biais de .NET 3.5. Quelques minutes avec le partage de la Source de la CLI montre que l'exécution implémente son propre minuterie file d'attente qui est similaire à la File d'attente du Minuteur Minuteries, mais jamais réellement appelle les fonctions Win32.
Il semble que l' .NET 4.0 runtime met également en œuvre sa propre file d'attente de minuterie. Mon programme de test (voir ci-dessous) fournit des résultats similaires .NET 4.0 comme il le fait en vertu de l' .NET 3.5. J'ai créé ma propre gestion de l'enveloppe pour la Minuterie de la File d'attente des Minuteries et prouvé que je peux obtenir de 1 ms (avec une assez bonne précision), donc, je considère qu'il est peu probable que je suis en train de lire la CLI source de mal.
J'ai deux questions:
Tout d'abord, quelles sont les causes de l'exécution de la mise en œuvre de la minuterie de la file d'attente pour être si lent? Je ne peux pas faire mieux que de 15 ms résolution et la précision semble être de l'ordre de -1 à +30 ms. C'est, si je demande 24 ms, je vais chercher les tiques n'importe où à partir de 23 à 54 ms d'intervalle. Je suppose que je pourrais passer plus de temps avec la CLI de la source de la trace de la réponse, mais pensé que quelqu'un ici pourrait savoir.
Deuxièmement, et je me rends compte que c'est plus difficile de répondre, pourquoi ne pas utiliser la Minuterie de la File d'attente des Chronomètres? Je me rends compte que .NET 1.x avait à exécuter sur Win9x, qui n'ont pas de ces Api, mais ils ont existé depuis Windows 2000, qui, si je me souviens bien, est l'exigence minimale .NET 2.0. Est-ce parce que la CLI a eu à courir sur la non-Windows boîtes?
Mon minuteries programme de test:
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();
}
}
}