Проходят ли таймеры C# в отдельном потоке?
делает система.Таймеры.Таймер истекает в отдельном потоке, чем поток, который его создал?
допустим у меня есть класс с таймер, который срабатывает каждые 5 секунд. Когда таймер срабатывает, в методе elapsed изменяется некоторый объект. Допустим, для изменения этого объекта требуется много времени, например 10 секунд. Возможно ли, что я столкнусь с конфликтами потоков в этом сценарии?
5 ответов:
это зависит. Элемент
System.Timers.Timerимеет два режима работы.если
SynchronizingObjectимеет значениеISynchronizeInvokeнапример тутElapsedсобытие будет выполняться в потоке, содержащем синхронизирующий объект. Как правило, этиISynchronizeInvokeэкземпляры-это не что иное, как обычный старыйControlиFormпримеры, с которыми мы все знакомы. Так что в таком случаеElapsedсобытие вызывается в потоке пользовательского интерфейса, и он ведет себя аналогичноSystem.Windows.Forms.Timer. В противном случае, это действительно зависит от конкретныхISynchronizeInvokeэкземпляр, который был использован.если
SynchronizingObjectnull тогдаElapsedсобытие вызывается наThreadPoolпоток и он ведет себя подобноSystem.Threading.Timer. На самом деле, он фактически используетSystem.Threading.Timerза кулисами и делает операцию маршалинга после он получает обратный вызов таймера, если это необходимо.
каждое прошедшее событие будет срабатывать в том же потоке, если не выполняется предыдущее прошедшее событие.
таким образом, он обрабатывает столкновение для вас
попробуйте поместить это в консоль
static void Main(string[] args) { Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); var timer = new Timer(1000); timer.Elapsed += timer_Elapsed; timer.Start(); Console.ReadLine(); } static void timer_Elapsed(object sender, ElapsedEventArgs e) { Thread.Sleep(2000); Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); }вы получите что-то вроде этого
10 6 12 6 12где 10-вызывающий поток, а 6 и 12 запускаются из события BG elapsed. Если вы удалите нить.Сон(2000); Вы получите что-то вроде этого
10 6 6 6 6Так нет столкновения.
но это все еще оставляет U с проблемой. если вы запускаете событие каждые 5 секунд, и для редактирования требуется 10 секунд, вам нужна блокировка, чтобы пропустить некоторые изменения.
Для Системы.Таймеры.Таймер, на отдельном потоке, если SynchronizingObject не установлен.
static System.Timers.Timer DummyTimer = null; static void Main(string[] args) { try { Console.WriteLine("Main Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId); DummyTimer = new System.Timers.Timer(1000 * 5); // 5 sec interval DummyTimer.Enabled = true; DummyTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnDummyTimerFired); DummyTimer.AutoReset = true; DummyTimer.Start(); Console.WriteLine("Hit any key to exit"); Console.ReadLine(); } catch (Exception Ex) { Console.WriteLine(Ex.Message); } return; } static void OnDummyTimerFired(object Sender, System.Timers.ElapsedEventArgs e) { Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); return; }выход вы увидите, если DummyTimer выстрелил на 5 секунд интервал:
Main Thread Id: 9 12 12 12 12 12 ...Итак, как видно, OnDummyTimerFired выполняется на рабочем потоке.
нет, дальнейшее усложнение - если вы уменьшите интервал, чтобы сказать 10 мс,
Main Thread Id: 9 11 13 12 22 17 ...это потому, что если предыдущее выполнение OnDummyTimerFired не выполняется при следующем тике, то .NET создаст новый нить для выполнения этой работы.
все усложняется еще больше,"Система.Таймеры.Класс Timer предоставляет простой способ справиться с этой дилеммой-он предоставляет свойство public SynchronizingObject. Установка этого свойства для экземпляра формы Windows (или элемента управления в форме Windows) гарантирует, что код в обработчике событий Elapsed выполняется в том же потоке, в котором был SynchronizingObject создать экземпляр."
Если прошедшее событие занимает больше времени, чем интервал, он создаст другой поток, чтобы вызвать прошедшее событие. Но есть обходной путь для этого
static void timer_Elapsed(object sender, ElapsedEventArgs e) { try { timer.Stop(); Thread.Sleep(2000); Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); } finally { timer.Start(); } }
Comments