Проходят ли таймеры C# в отдельном потоке?



делает система.Таймеры.Таймер истекает в отдельном потоке, чем поток, который его создал?

допустим у меня есть класс с таймер, который срабатывает каждые 5 секунд. Когда таймер срабатывает, в методе elapsed изменяется некоторый объект. Допустим, для изменения этого объекта требуется много времени, например 10 секунд. Возможно ли, что я столкнусь с конфликтами потоков в этом сценарии?

176   5  

5 ответов:

на

это зависит. Элемент System.Timers.Timer имеет два режима работы.

если SynchronizingObject имеет значение ISynchronizeInvoke например тут Elapsed событие будет выполняться в потоке, содержащем синхронизирующий объект. Как правило, эти ISynchronizeInvoke экземпляры-это не что иное, как обычный старый Control и Form примеры, с которыми мы все знакомы. Так что в таком случае Elapsed событие вызывается в потоке пользовательского интерфейса, и он ведет себя аналогично System.Windows.Forms.Timer. В противном случае, это действительно зависит от конкретных ISynchronizeInvoke экземпляр, который был использован.

если SynchronizingObject null тогда 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 создать экземпляр."

http://msdn.microsoft.com/en-us/magazine/cc164015.aspx#S2

Если прошедшее событие занимает больше времени, чем интервал, он создаст другой поток, чтобы вызвать прошедшее событие. Но есть обходной путь для этого

static void timer_Elapsed(object sender, ElapsedEventArgs e)    
{     
   try
   {
      timer.Stop(); 
      Thread.Sleep(2000);        
      Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);    
   }
   finally
   {
     timer.Start();
   }
}
    Ничего не найдено.

Добавить ответ:
Отменить.