Выполните System.Таймеры.Таймер выполняется в независимых потоках?

#c# #.net #timer

#c# #.net #таймер

Вопрос:

Я пытаюсь понять, когда System.Timers.Timer возникает прошедшее событие, возникает ли оно в независимом потоке?

Мой приведенный ниже пример, похоже, предполагает, что три таймера работают независимо в своих собственных потоках:

 class Program
{
    static System.Timers.Timer timer = new System.Timers.Timer();
    static System.Timers.Timer timer2 = new System.Timers.Timer();
    static System.Timers.Timer timer3 = new System.Timers.Timer();

    static void Main(string[] args)
    {
        timer.Elapsed  = new System.Timers.ElapsedEventHandler(
            timer_Elapsed);
        timer2.Elapsed  = new System.Timers.ElapsedEventHandler(
            timer2_Elapsed);
        timer3.Elapsed  = new System.Timers.ElapsedEventHandler(
            timer3_Elapsed);

        timer.Interval = 1000;
        timer2.Interval = 1000;
        timer3.Interval = 1000;

        timer.Start();
        timer2.Start();
        timer3.Start();

        Console.WriteLine("Press 'q' to quit the sample.");
        while (Console.Read() != 'q') ;
    }

    static void timer3_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        timer3.Stop();
        Console.WriteLine("Timer 3 Hit...");            
        timer3.Start();
    }

    static void timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        timer2.Stop();
        Console.WriteLine("Timer 2 Hit...");
        Thread.Sleep(2000);
        timer2.Start();
    }

    static void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        timer.Stop();
        Console.WriteLine("Timer 1 Hit...");
        Thread.Sleep(10000);
        timer.Start();
    }
}
 

введите описание изображения здесь

Комментарии:

1. Да, они выполняются в другом потоке.

2. System.Timers.Timer Использует a System.Threading.Timer (который выполняет один метод обратного вызова в потоке пула потоков) внутри. Смотрите Исходный код .

Ответ №1:

Согласно MSDN, System.Timers.Timer при возникновении Elapsed события оно вызывается в потоке в системном пуле потоков:

Если свойство SynchronizingObject равно Nothing , событие Elapsed вызывается в потоке ThreadPool. Если обработка прошедшего события длится дольше, чем интервал, событие может быть вызвано снова в другом потоке ThreadPool. В этой ситуации обработчик событий должен быть реентерабельным.

Поскольку значение по умолчанию SynchronizingObject равно null , все ваши прошедшие события будут обрабатываться в пуле потоков. Итак, это зависит от того, насколько заполнен пул потоков, если есть свободные потоки, то каждое прошедшее событие, скорее всего, может выполняться одновременно в отдельных потоках. Однако, если по какой-то причине системный пул потоков уже полностью используется, возможно, прошедшие события могут быть сериализованы по мере их планирования.

Главное: «это зависит». То есть им будет разрешено работать параллельно, пока в пуле есть свободные потоки.

Ссылка: MSDN в системе.Таймеры.Таймер

Комментарии:

1. Все ответы были правильными, однако я чувствую, что здесь ключевым компонентом является ThreadPool.

Ответ №2:

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

Вы могли бы выводить System.Threading.Thread.CurrentThread.ManagedThreadId данные в каждом из них, чтобы знать наверняка.

Ответ №3:

Это довольно сложно. В документации говорится следующее:

Серверный таймер предназначен для использования с рабочими потоками в многопоточной среде. Серверные таймеры могут перемещаться между потоками для обработки вызванного прошедшего события, что обеспечивает большую точность, чем таймеры Windows, при своевременном вызове события.

и тогда это:

Если свойство SynchronizingObject равно null, истекшее событие вызывается в потоке ThreadPool. Если обработка прошедшего события длится дольше, чем интервал, событие может быть вызвано снова в другом потоке ThreadPool. В этой ситуации обработчик событий должен быть реентерабельным.

и тогда это:

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

Итак, нет простого ответа на ваш вопрос «возникает ли он в независимом потоке?» Это зависит от многих вещей.

Ответ №4:

Да, каждый раз, когда вызывается Elapsed , обратный вызов запускается в своем собственном потоке.

Кроме того, ничто не мешает запустить один обработчик прошедших событий до завершения предыдущего. Например, если ваш таймер срабатывает каждые 500 миллисекунд, но выполнение кода обработчика прошедших событий занимает 2 секунды, истекший код может обращаться к тем же ресурсам (не потокобезопасным объектам, файлам и т. Д.).