#c# #performance #timer
#c# #Производительность #таймер
Вопрос:
using System;
using System.Threading;
internal class TimerQueueTimer : IDisposable
{
public TimerQueueTimer(int interval, int msBeforeFirstCall)
{
this.interval = interval;
this.msBeforeFirstCall = msBeforeFirstCall;
this.callback = this.ticked;
this.isTheFirstTick = true;
this.isStopped = true;
}
public event EventHandler Ticked;
public void Start()
{
if (!this.isStopped)
{
return;
}
this.isTheFirstTick = true;
this.isStopped = false;
Computer.ChangeTimerResolutionTo(1);
NativeMethods.CreateTimerQueueTimer(
out this.handle,
IntPtr.Zero,
this.callback,
IntPtr.Zero,
(uint)this.msBeforeFirstCall,
(uint)this.interval,
CallbackExecution.ExecuteInTimerThread);
}
public void Stop()
{
if (this.isStopped)
{
return;
}
NativeMethods.DeleteTimerQueueTimer(
IntPtr.Zero,
this.handle,
IntPtr.Zero);
Computer.ClearTimerResolutionChangeTo(1);
this.isStopped = true;
}
public void Dispose()
{
this.Stop();
}
private void ticked(IntPtr parameterPointer, bool timerOrWaitFired)
{
if (this.isStopped)
{
return;
}
if (this.isTheFirstTick)
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
}
this.isTheFirstTick = false;
var ticked = this.Ticked;
if (ticked != null)
{
ticked(this, EventArgs.Empty);
}
}
private IntPtr handle;
private volatile bool isStopped;
private volatile bool isTheFirstTick;
private readonly WaitOrTimerDelegate callback;
private readonly int interval;
private readonly int msBeforeFirstCall;
}
(Примечание: Computer.ChangeTimerResolutionTo()
и Computer.ClearTimerResolutionChangeTo()
вызывают timeBeginPeriod
и timeEndPeriod
соответственно.)
Вопросы:
- Обратный вызов выполняется в потоке таймера, а не в потоке ThreadPool. Это нормально, если функция обратного вызова выполняется быстро, верно?
- Делает ли установка приоритета потока обратного вызова (и, следовательно, потока таймера) наивысшим что-нибудь с точки зрения производительности?
- Было бы лучше установить интервал таймера в 1 мс и подсчитывать такты, повышая
Ticked
iftickCount % interval == 0
? Является ли таймер с меньшим интервалом более точным? - Есть ли какая-либо причина, по которой это может быть менее точным и / или выверенным, чем аналогично созданный
timeSetEvent
таймер?
Причина, по которой я спрашиваю, заключается в том, что мы сталкиваемся с проблемами, когда обратный вызов таймера иногда задерживается на ~ 50 мс, когда система находится под большой нагрузкой. По сравнению с тем, когда мы использовали ранее timeSetEvent
, казалось, что это происходило реже — хотя это может быть просто иллюзией. Я знаю, что Windows не является детерминированной, поэтому я могу сделать не так много. Тем не менее, я хочу убедиться, что я сделал все, что мог, чтобы сделать это как можно более высокоприоритетным. Могу ли я сделать что-нибудь еще?
Комментарии:
1. К вашему сведению: проверять, нужно ли
ticked == null
сначала только для прямого вызоваTicked
события, на самом деле не лучший ход. Обычно весь смысл локальной переменной заключается в том, чтобы избежать состояния гонки.
Ответ №1:
Я использовал приоритетную очередь для решения этой проблемы: каждый элемент очереди содержит адрес обратного вызова (подпрограмму таймера), указатель на параметры обратного вызова и время в будущем, когда он должен быть запущен.
«Время» является приоритетом, логика здесь заключается в том, чтобы иметь возможность разбудить поток таймера из другого потока. Когда обратный вызов добавляется в очередь другим потоком, поток таймера просыпается и ищет верхний элемент приоритетной очереди, вычисляет разницу между текущим временем и «временем», хранящимся в очереди, и переходит в режим ожидания до тех пор, пока не превысит рассчитанный тайм-аут.
Когда поток таймера пробуждается по таймауту, он запускает новый поток из пула потоков, который вызывает обратный вызов.
Здесь у меня есть реализация очереди таймера, она недостаточно хорошо протестирована, но вы можете посмотреть, поможет ли это.
Комментарии:
1. @ruffp было бы здорово, если бы вы могли убрать -ve point, если считаете, что я решил вашу проблему 🙂
2. Спасибо @ruffp за редактирование и за то, что сделали ответ более читабельным. В дальнейшем я постараюсь дать четкие ответы.