Рубрики
Программирование

Типы таймеров в C#

Представить себе задачу, для которой нужен таймер несложно, но вот зачем нужны разные их виды? Ведь на первый взгляд таймер настолько очевидный и простой концепт, что трудно представить возможные и при этом существенные различия между их реализациями. Однако, в C# на данный момент предоставляет аж 6 классов реализующих таймер. Давайте же рассмотрим их подробней и поймём в чём разница между ними.

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

System.Windows.Threading.DispatcherTimer Для интерфейса в WPF 
System.UI.XAML.DispatcherTimer Для UWP приложений
System.Web.UI.Timer Для Web
System.Windows.Form.TimerДля WinForms
System.Timers.TimerДля серверного приложения (и унаследован от компонента)
System.Threading.TimerБазовый таймер 

Таб.1 Список таймеров C#

Рассмотрим каждый из них поподробней и начнём с базового таймера.

При использовании System.Threading.Timer в первом параметре конструктора передаётся метод-слушатель, который будет вызван при срабатывании таймер. На этот метод накладываются определённые требования делегата TimerCallback, согласно которым возвращаемым типом должен быть void, а типом параметра — object.

  static void CallBackMethod(object state)  

Также конструктор может содержать другие параметры:

  • втором параметре может передаваться любой объект, который затем будет получен с аргументом объекта в методе обратного вызова
  • третий параметр определяет промежуток времени через которые callback должен быть вызван повторно
  • в четвертом параметре должно быть указано значение -1.

Для изменения промежутка времени после создания объекта Timer служит метод Change(). В целом этого достаточно, для того, чтобы начать использовать данный таймер.

Данный таймер чаще всего встречается в консольных приложениях и, пожалуй, является самым одним из первых таймеров с которым знакомятся разработчики. При этом, что иронично, System.Threading.Timer не является потокобезопасным, в отличии от следующего.

System.Timers.Timer по сути является обёрткой над System.Threading.Timer, которая предоставляет некоторые дополнительные функции, используемые для диспетчеризации в определенном потоке, в частности, добавление/удаление слушателей после инициализации. Другим же важным отличием является то, что данный таймер также наследуется от класса Component. Это позволяет использовать его в UI, однако для этого лучше подходят другие таймеры.

System.Windows.Form.Timer, System.Web.UI.Timer, System.UI.XAML.DispatcherTimer и System.Windows.Threading.DispatcherTimer созданы для использования в настольных графических и веб приложениях. В целом же их можно рассматривать как один таймер, поскольку они крайне похожи в применении и разные классы нужны для того, чтобы обеспечить реализацию одних и тех же свойств и методов в различных видах приложений. Общего же у них – поддержка размещения IComponent в IContainer,

В общем виде и для большей наглядности можно свести основные отличия между таймерами в следующую таблицу:

Описание возможностейSystem.
Timers
System.
Threading
System.
Windows.
Forms
System.
Web.UI
System.
Windows.
Threading
System.
UI.XAML
Поддерживает добавление/удаление слушателя после создания экземпляра таймераДаНетДаДа ДаДа
Поддерживает обратный вызов из UI-потокаДаНетДаДа ДаДа
Обратный вызов потоков вызванных из пула потоковДаДаНетНет НетНет
Поддержка drag-and-drop в UI редактореДаНетДаНет ДаДа
Подходит для работы в многопоточной среде сервераДаДаНетНет  НетНет
Включает поддержку передачи произвольного состояния от инициализации таймера к обратному вызовуНетДаНет Нет НетНет
Поддержка размещения IComponent в IContainerДаНетДаДа ДаДа

Таб. 2 Отличия таймеров

При этом, несмотря на все отличия у всех таймеров есть и общие места, такие как реализация IDisposable, поддержка разовых и периодически повторяющиеся обратных вызовов, возможность доступа через границы домена приложения. Таким образом, можно сказать, что взаимодействие с различными таймерам весьма похоже и неопытный разработчик может даже не заметить отличий в некоторых ситуациях и использовать не совсем подходящие таймеры.

При этом, также стоит отметить, что разные типы таймеров по разному работают с потоками. Так например при использовании System.Timers.Timer работа с ним всегда выносится в отдельный поток, общий для всех таймеров данного типа (т.е. при использовании нескольких таймеров будет создан лишь один поток). Это необходимо для того, чтобы с одной стороны блокировка исходного потока (например, трудоёмкими вычислениями или запросами) не влияла на работу таймеров, а с другой для экономии ресурсов на создании и поддержании работы потоков. А вот остальные таймеры “живут” не в отдельном потоке, а UI-потоке, что позволяет им иметь доступ к элементам интерфейса.

Таким образом можно сформулировать следующие рекомендации по применению таймеров:

  1. Для приложений без UI по умолчанию лучше использовать более легковесный System.Threading.Timer, но если требуется потокобезопасность System.Timers.Timer будет лучшим выбором.
  2. Если нужно использование в UI, то следует применять соответствующий таймер, что позволит взаимодействовать с элементами UI напрямую.
  3. System.Timers.Timer может использоваться в UI, однако так как он является асинхронным, то выполняется в отдельном потоке и это вызывает ряд сложностей при работе с компонентами UI и переменными.

В завершении также скажу пару слов о .Net Core. Поскольку на данный момент .Net Core не поддерживает создание десктоп-приложений с графическим интерфейсом из коробки и без применения, то и соответствующих таймеров там нет (собственно получается, что и не надо). Однако, в .NET Core 3.0 обещают поддержку Windows Forms  и WPF в компоненте Windows Desktop и вот там скорее всего они будут. При этом важно понимать, что этот компонент будет доступен только для ОС Windows.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *