Допустим, у меня есть существующий экземпляр System.Threading.Timer, и я хотел бы вызвать на нем Change, чтобы вернуть время срабатывания:
var timer = new Timer(DelayCallback, null, 10000, Timeout.Infinite);
// ... (sometime later but before DelayCallback has executed)
timer.Change(20000, Timeout.Infinite);
Я использую этот таймер для выполнения «обратного вызова в режиме ожидания» после периода бездействия. («Бездействие» и «отсутствие активности» в данном случае являются условиями, определяемыми приложением ... детали не так уж и важны.) Каждый раз, когда я выполняю «действие», я хочу сбросить таймер, чтобы он всегда устанавливался. выстрелить через 10 секунд после этого.
Однако существует внутреннее состояние гонки, потому что, когда я вызываю Change, я не могу определить, сработал ли таймер, на основе его старых настроек. (Я, конечно, могу сказать, произошел ли мой обратный вызов, но я не могу сказать, поставил ли поток внутреннего таймера CLR в очередь мой обратный вызов пулу потоков и его выполнение неизбежно.)
Теперь я знаю, что могу вызывать Dispose для экземпляра таймера и воссоздавать его каждый раз, когда мне нужно «вернуть его обратно». но этот кажется менее эффективен, чем просто изменение существующего таймера. Конечно, это не май ... Я проведу несколько микротестов и дам вам знать.
В качестве альтернативы я всегда могу отслеживать ожидаемое время срабатывания (через DateTime.Now.AddSeconds (10)) и, если срабатывает исходный таймер, игнорировать его, проверяя DateTime.Now в обратном вызове. (Меня беспокоит то, что это может быть не на 100% надежным из-за таймера, использующего TimeSpan, и моей проверки с использованием DateTime ... это может не быть проблемой, но по какой-то причине мне это не совсем удобно ... )
Мои вопросы:
Этот вопрос носит несколько гипотетический характер, поскольку у меня уже есть несколько рабочих решений (на основе Dispose и на основе DateTime.Now) ... Меня в основном интересуют предложения, связанные с производительностью (поскольку я буду "отталкивать "Таймер ОЧЕНЬ часто").
Спасибо!





На самом деле мне пришлось создать свой собственный класс «Тайминга» для созданной мной MMORPG. Он мог отслеживать более 100 000 «сущностей», у которых были таймеры для обработки ИИ и других задач. Из-за различных действий, которые можно было предпринять, мне пришлось бы на мгновение отложить мероприятие.
Мой урок по таймингу был полностью написан вручную, так что это не совсем то, что вы ищете. Но кое-что, что вы могли бы сделать, было бы похоже на решение, которое я придумал, - это сделать что-то вроде:
while (sleepyTime > 0)
{
int temp = sleepyTime;
sleepyTime = 0;
Thread.Sleep(temp);
}
// here's where your actual code is.
Затем вы можете создать метод "Delay", который в основном просто рекламирует sleepyTime.
похоже, что вы действительно хотите, чтобы событие простоя приложения
System.Windows.Forms.Application.Idle
Я интерпретирую ваши вопросы как запрос на реализацию интерфейса IdleNotifier, указанного ниже. Также вы заявляете, что ActionOccured () должен быть быстрым.
public delegate void IdleCallback();
public interface IdleNotifier
{
// Called by threadpool when more than IdleTimeSpanBeforeCallback
// has passed since last call on ActionOccured.
IdleCallback Callback { set; }
TimeSpan IdleTimeSpanBeforeCallback { set; }
void ActionOccured();
}
Ниже я предлагаю реализацию с помощью System.Threading.Timer. Важные моменты по реализации:
Выполнение:
public class IdleNotifierTimerImplementation : IdleNotifier
{
private readonly object SyncRoot = new object();
private readonly Timer m_Timer;
private IdleCallback m_IdleCallback = null;
private TimeSpan m_IdleTimeSpanBeforeEvent = TimeSpan.Zero;
// Null means there has been no action since last idle notification.
private DateTime? m_LastActionTime = null;
public IdleNotifierTimerImplementation()
{
m_Timer = new Timer(OnTimer);
}
private void OnTimer(object unusedState)
{
lock (SyncRoot)
{
if (m_LastActionTime == null)
{
m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero);
return;
}
TimeSpan timeSinceLastUpdate = DateTime.UtcNow - m_LastActionTime.Value;
if (timeSinceLastUpdate > TimeSpan.Zero)
{
// We are no idle yet.
m_Timer.Change(timeSinceLastUpdate, TimeSpan.Zero);
return;
}
m_LastActionTime = null;
m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero);
}
if (m_IdleCallback != null)
{
m_IdleCallback();
}
}
// IdleNotifier implementation below
public void ActionOccured()
{
lock (SyncRoot)
{
m_LastActionTime = DateTime.UtcNow;
}
}
public IdleCallback Callback
{
set
{
lock (SyncRoot)
{
m_IdleCallback = value;
}
}
}
public TimeSpan IdleTimeSpanBeforeCallback
{
set
{
lock (SyncRoot)
{
m_IdleTimeSpanBeforeEvent = value;
// Run OnTimer immediately
m_Timer.Change(TimeSpan.Zero, TimeSpan.Zero);
}
}
}
}
В этом коде есть много простых улучшений производительности.
Если кого-то заинтересуют мои первые мысли по этому поводу, просто спросите меня.