Как использовать таймер остановки / запуска против SynchronizingObject и блокировки для System.Timers.Timer?
Верхнее примечание: по комментариям я могу предположить, что все рабочие решения с этой запиской действительно в моем приложении я пытаюсь объединить их. Кстати, перед тем, как понизить голосование, вы можете спросить меня о деталях.
Верхнее примечание 2: Кстати, поскольку SO отличается от отрицательных голосов MSO или закрытых голосов, поощряет удаление вопроса, я не буду, в противном случае все ценные комментарии и ответы будут удалены. Здесь есть место, чтобы помочь и попытаться понять друг друга
Вот самая основная из 4 различных реализаций кодов linqpad. За исключением первого все остальные дают желаемый результат.
Можете ли вы объяснить детали для них?
Поскольку в моем приложении много таймеров, мне нужно управлять и синхронизировать полный код, который лучше всего использовать, и каковы преимущества / недостатки альтернативных решений.
Без ни SynchronizingObject, ни таймера остановки / запуска, ни блокировки
System.Timers.Timer timer2 = new System.Timers.Timer(100);
int i = 0;
void Main()
{
timer2.Elapsed += PromptForSave;
timer2.Start();
}
private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
i = i + 1;
Thread.Sleep(new Random().Next(100, 1000));
Console.WriteLine(i);
}
дает:
4 5 6 7 8 9 11 12 13 14 15 15 15 17 18 20 21 22
С SynchronizingObject:
void Main()
{
timer2.Elapsed += PromptForSave;
timer2.SynchronizingObject = new Synchronizer();
timer2.Start();
}
private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
i = i + 1;
Thread.Sleep(new Random().Next(100, 1000));
Console.WriteLine(i);
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
С таймером Старт / Стоп
void Main()
{
timer2.Elapsed += PromptForSave;
timer2.Start();
}
private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
timer2.Stop();
i = i + 1;
Thread.Sleep(new Random().Next(100, 1000));
Console.WriteLine(i);
timer2.Start();
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
наконец с замком
object lockForTimer = new object();
void Main()
{
timer2.Elapsed += PromptForSave;
timer2.Start();
}
private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
lock(lockForTimer){
i = i + 1;
Thread.Sleep(new Random().Next(100, 1000));
Console.WriteLine(i);
timer2.Start();
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Синхронизатор выглядит так:
public class Synchronizer : ISynchronizeInvoke
{
private Thread m_Thread;
private BlockingCollection<Message> m_Queue = new BlockingCollection<Message>();
public Synchronizer()
{
m_Thread = new Thread(Run);
m_Thread.IsBackground = true;
m_Thread.Start();
}
private void Run()
{
while (true)
{
Message message = m_Queue.Take();
message.Return = message.Method.DynamicInvoke(message.Args);
message.Finished.Set();
}
}
public IAsyncResult BeginInvoke(Delegate method, object[] args)
{
Message message = new Message();
message.Method = method;
message.Args = args;
m_Queue.Add(message);
return message;
}
public object EndInvoke(IAsyncResult result)
{
Message message = result as Message;
if (message != null)
{
message.Finished.WaitOne();
return message.Return;
}
throw new ArgumentException("result");
}
public object Invoke(Delegate method, object[] args)
{
Message message = new Message();
message.Method = method;
message.Args = args;
m_Queue.Add(message);
message.Finished.WaitOne();
return message.Return;
}
public bool InvokeRequired
{
get { return Thread.CurrentThread != m_Thread; }
}
private class Message : IAsyncResult
{
public Delegate Method = null;
public object[] Args = null;
public object Return = null;
public object State = null;
public ManualResetEvent Finished = new ManualResetEvent(false);
public object AsyncState
{
get { return State; }
}
public WaitHandle AsyncWaitHandle
{
get { return Finished; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return Finished.WaitOne(0); }
}
}
}
1 ответ
4-й раствор (
lock
) опасный. Так какElapsed
событие будет поднят наThreadPool
каждый раз, и вы можете заблокировать многие из них одновременно, это может привести кThreadPool
расти (со всеми вытекающими последствиями). Так что это плохое решение.Третье решение (запуск / остановка) будет обрабатывать события не со скоростью, установленной таймером, а со скоростью, которая зависит от того, сколько времени занимает каждое конкретное действие. Так что это может "пропустить" многие события. Это решение похоже на "падение кадра" в потоковом видео.
Второе решение поставит в очередь все действия и не пропустит их. Это потенциально опасно, когда время обработки действия (почти всегда) превышает интервал таймера. Очередь будет только расти, вызывая
OutOfMemoryException
в какой-то момент. Это решение похоже на "буфер кадров" в потоковом видео.Первый должен быть удален, есть только проблемы с этим.
Поэтому вам следует выбирать между 2-м и 3-м решениями в зависимости от того, что важно для вашего варианта использования: надежная обработка всех входящих событий или обработка с максимально возможной пропускной способностью (скоростью).