DispatchTimer - предотвращает запуск события тика, если предыдущий тик все еще работает - PullRequest
4 голосов
/ 18 октября 2011

В приложении Silverlight у меня есть блок кода, который должен запускаться каждые 500 мс. Я планирую использовать DispatcherTimer для достижения этой цели (см. Код ниже).

DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 500); // 500 Milliseconds
dt.Tick += new EventHandler(dt_Tick);
dt.Start();

Однако может случиться, что выполнение блока кода занимает более 500 мс (блок кода выполняет некоторые вызовы веб-службы). Как мне убедиться, что, если вызов в данный момент выполняется, DispatcherTimer не вызывает другое событие? Какие есть варианты и как лучше? Используя замки?

Ответы [ 4 ]

5 голосов
/ 18 октября 2011

Только DispatcherTimer работает в потоке диспетчера - поэтому вы не сможете запустить два обработчика одновременно. Возможно, они будут поставлены в очередь и будут запускаться один за другим, разумеется - вам следует проверить.

Тем не менее, вы не должны делать вызов веб-службы в DispatcherTimer в любом случае. Сделайте это в фоновом потоке, иначе вы блокируете пользовательский интерфейс для обновления все время, пока вы ждете веб-службы. По сути, вы не должны выполнять длительную работу в потоке пользовательского интерфейса. Используйте один из различных других таймеров (например, System.Timers.Timer) для регулярного выполнения работы над потоком пула потоков и используйте диспетчер для обратного вызова потока пользовательского интерфейса, когда у вас есть данные, которые необходимо отображается в пользовательском интерфейсе.

Конечно, теперь у вас есть потенциальная проблема нового типа таймера, запускающего несколько раз одновременно в нескольких потоках. Один из способов избежать этого - установить для свойства AutoReset значение false и просто запланировать следующий тик таймера в конце текущего.

1 голос
/ 18 октября 2011

Я не знаю, есть ли у DispatchTimer какой-нибудь умный способ сделать это, но в этой ситуации я бы не пытался заставить таймер не запускать событие, а заставить событие ничего не делать, если его нет закончил предыдущий прогон.

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

Нужный метод: Monitor.TryEnter , и вы захотите убедиться, что вы правильно обрабатываете ошибки, как и при любом использовании блокировок.

1 голос
/ 18 октября 2011

Чтобы событие прошло успешно, без повторного вызова от вашего DispatcherTimer, когда в предыдущем тике завершается stop, таймер диспетчера после входа в событие dt_Tick и в конце события тика вызывает start снова, что инициализирует IsEnabled DispatcherTimer снова в true.

1 голос
/ 18 октября 2011

Я бы сказал, что вы пропустите тик, если это займет слишком много времени, иначе вы получите огромную очередь из-за блокировки.

Так в обработчике событий скажите:

if(!busy) {
  busy = true;

  // some code which could take longer than 500 ms

  busy = false;
}
...