Почему? are.NET таймеры ограничены разрешением 15 мс?
обратите внимание, что я спрашиваю о чем-то, что будет вызывать функцию обратного вызова чаще, чем раз в 15 мс, используя что-то вроде System.Threading.Timer. Я не спрашиваю о том, как точно рассчитать кусок кода, используя что-то вроде System.Diagnostics.Stopwatch или даже QueryPerformanceCounter.
кроме того, я прочитал соответствующие вопросы:
точный таймер Windows? Система.Таймеры.Таймер() ограничен 15 мс
таймер высокого разрешения в .NET
ни один из которых не поставляет a полезный ответ на мой вопрос.
кроме того, рекомендуемая статья MSDN,реализовать постоянно обновляющийся поставщик времени с высоким разрешением для Windows, речь идет о синхронизации вещей, а не о предоставлении непрерывного потока тиков.
С, что сказал. . .
там очень много плохой информации об объектах таймера .NET. Например, System.Timers.Timer выставляется как " высокопроизводительный таймер, оптимизированный для серверных приложений." И System.Threading.Timer - Это почему-то считается гражданином второго класса. Общепринятая мудрость заключается в том, что System.Threading.Timer это обертка вокруг Windows Таймеры Очереди Таймеров и System.Timers.Timer это что-то совсем другое.
реальность сильно отличается. System.Timers.Timer это просто тонкий компонент обертки вокруг System.Threading.Timer (просто используйте отражатель или 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. Это потому, что CLI должен был работать на окнах без окон?
моя программа тестирования таймеров:
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 ответов:
возможно, документ, связанный здесь, немного объясняет это. Это своего рода сухой, так что я только просмотрел его быстро :)
цитируя вступление:
разрешение системного таймера определяет как часто Windows выполняет две основные действия:
- обновить ТИК таймера подсчитайте, если полный ТИК прошел.
- проверьте, является ли запланированный объект таймера истек.
ТИК таймера-это понятие прошедшего время что Windows использует для отслеживания время суток и квантовое время потока. По умолчанию, прерывание часов и ТИК таймера тот же, но Windows или приложение может изменить часы период прерывания.
по умолчанию таймер разрешение на Windows 7 15.6 миллисекунды (МС). Некоторые приложения уменьшить до 1 мс, что снижает время работы батареи на мобильных системах целых 25 процентов.
родом из: Таймеры, Таймер Разрешение и разработка эффективного кода (docx).
разрешение таймера задается пульсом системы. Обычно это по умолчанию до 64 уд/s, что в 15.625 МС. Однако существуют способы, чтобы изменить эти параметры системы для достижения резолюции таймера до 1 мс или даже до 0,5 мс на новых платформах:
1. Переход на разрешение 1 мс с помощью интерфейса мультимедийного таймера:
интерфейс таймера мультимедиа может обеспечить разрешение до 1 мс. Смотрите О Мультимедиа Таймеры (MSDN),получение и настройка разрешения таймера (MSDN), и этой ответьте для получения более подробной информации о
timeBeginPeriod. Примечание: не забудьте позвонить в timeEndPeriod чтобы вернуться к разрешению таймера по умолчанию, когда это будет сделано.как делать:
#define TARGET_RESOLUTION 1 // 1-millisecond target resolution TIMECAPS tc; UINT wTimerRes; if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { // Error; application can't continue. } wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); timeBeginPeriod(wTimerRes); // do your stuff here at approx. 1 ms timer resolution timeEndPeriod(wTimerRes);Примечание: эта процедура доступна и для других процессов, и полученное разрешение применяется во всей системе. Самое высокое разрешение, запрошенное любым процесс будет активным, учитывайте последствия.
2. Переход на разрешение 0,5 мс:
вы может получить 0,5 мс разрешение с помощью скрытого API
NtSetTimerResolution(). NtSetTimerResolution экспортируется собственной библиотекой Windows NT NTDLL.файл DLL. Смотрите Как установить разрешение таймера до 0,5 мс ? на MSDN. Тем не менее, истинное достижимое разрешение определяется базовым оборудованием. Современное оборудование поддерживает 0,5 мс разрешение. Еще больше подробностей можно найти в внутри таймеров высокого разрешения Windows NT. Поддерживаемые разрешения можно получить с помощью вызова NtQueryTimerResolution ().как делать:
#define STATUS_SUCCESS 0 #define STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000245 // after loading NtSetTimerResolution from ntdll.dll: // The requested resolution in 100 ns units: ULONG DesiredResolution = 5000; // Note: The supported resolutions can be obtained by a call to NtQueryTimerResolution() ULONG CurrentResolution = 0; // 1. Requesting a higher resolution // Note: This call is similar to timeBeginPeriod. // However, it to to specify the resolution in 100 ns units. if (NtSetTimerResolution(DesiredResolution ,TRUE,&CurrentResolution) != STATUS_SUCCESS) { // The call has failed } printf("CurrentResolution [100 ns units]: %d\n",CurrentResolution); // this will show 5000 on more modern platforms (0.5ms!) // do your stuff here at 0.5 ms timer resolution // 2. Releasing the requested resolution // Note: This call is similar to timeEndPeriod switch (NtSetTimerResolution(DesiredResolution ,FALSE,&CurrentResolution) { case STATUS_SUCCESS: printf("The current resolution has returned to %d [100 ns units]\n",CurrentResolution); break; case STATUS_TIMER_RESOLUTION_NOT_SET: printf("The requested resolution was not set\n"); // the resolution can only return to a previous value by means of FALSE // when the current resolution was set by this application break; default: // The call has failed }Примечание: функциональность NtSetTImerResolution в основном сопоставляется с функциями
timeBeginPeriodиtimeEndPeriodС помощью значения boolSet(см. внутри таймеров высокого разрешения Windows NT для более подробной информации о схеме и всех ее последствиях). Тем не менее, набор мультимедиа ограничивает точность до миллисекунды и NtSetTimerResolution позволяет установить суб-миллисекундных значения.
все повторы здесь о разрешении системного таймера. но .чистая таймеры не уважать его. Как заметил сам автор:
что среда выполнения реализует свою собственную очередь таймера, аналогичную таймеры очереди таймера, но никогда на самом деле не вызывает функции Win32.
и Ян указал на комментарий.
Итак, ответы выше-это хорошая информация, но не напрямую связанная с тайм-драйверами .net и поэтому вводят в заблуждение людей: (
короткий ответ на оба вопроса автора конструкция. Почему они решили пойти этим путем? Боялись за производительность всей системы? Кто знает...
Чтобы не дублировать, см. дополнительную информацию по обоим вопросам (и способы реализации точного таймеры на .net) в Яне коррелировали темы.
Comments