Выполняются ли таймеры С# в отдельном потоке?

Выполняется ли в System.Timers.Timer отдельный поток, чем поток, который его создал?

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

Ответ 2

Это зависит. 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 за кулисами и выполняет операцию маршалинга после получения обратного вызова таймера, если это необходимо.

Ответ 3

Каждое прошедшее событие будет срабатывать в том же потоке, если только предыдущий Истекший не запущен.

Таким образом, он обрабатывает столкновение для вас

попробуйте поместить его в консоль

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 события. Если вы удалите Thread.Sleep(2000); вы получите что-то вроде этого

10
6
6
6
6

Так как никаких столкновений нет.

Но это все еще оставляет проблему с проблемой. если u запускает событие каждые 5 секунд, и для редактирования требуется 10 секунд, вам нужно заблокировать некоторые пропуски.

Ответ 4

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

static void timer_Elapsed(object sender, ElapsedEventArgs e)    
{     
   try
   {
      timer.Stop(); 
      Thread.Sleep(2000);        
      Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);    
   }
   finally
   {
     timer.Start();
   }
}

Ответ 5

Для System.Timers.Timer, в отдельном потоке, если 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 выполняется в потоке Workers.

Нет, дальнейшее усложнение. Если вы сократите интервал до 10 мс,

Main Thread Id: 9
   11
   13
   12
   22
   17
   ... 

Это связано с тем, что если предыдущее исполнение OnDummyTimerFired не выполняется при следующем запуске следующего тика, тогда .NET создаст новый поток для выполнения этой задачи.

Далее усложняется: "Класс System.Timers.Timer предоставляет простой способ справиться с этой дилеммой - он предоставляет общедоступное свойство SynchronizingObject. Установка этого свойства в экземпляр Windows Form (или элемент управления в Windows Form), убедитесь, что код в обработчике событий Elapsed работает в том же потоке, на котором был создан экземпляр SynchronizingObject.

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