2013-08-13 3 views
0

У меня сложный вопрос (для меня как минимум). Я работаю над службой Windows, написанной на VB.net. Я использую класс System.Timers.Timer для периодического вызова метода делегата, чтобы узнать, есть ли какая-либо работа. Время обработки не является критическим, и я попытался предотвратить повторный вход в рабочий метод, отключив Таймер, как только метод будет вызван, и снова запустив его в конце.Marshalling call to main thread from System.Timers.Timer

Однако класс таймера Истекшие события происходят в другом потоке. Поиск в Интернете, большинство людей используют формы Windows, которые реализуют интерфейс ISynchronize, чтобы маршировать вызов назад к исходному потоку. В идеале я не хочу использовать Windows Forms для этого. Есть ли простой способ перенаправить вызов обратно в исходный поток?

В качестве альтернативы есть класс каркаса, который я могу наследовать, чтобы сделать это? Или в худшем случае простая реализация ISynchronize?

Imports System.Timers 
Imports System.IO 
Imports System.Data 


Public Class Application 
    Implements IDisposable 


Private WithEvents _Timer As Timer 



Private Sub SleepTimerCallback(sender As Object, e As ElapsedEventArgs) Handles _Timer.Elapsed 

    ' TODO need to find a way to bring this method back on the main thread. 

    ' Temporarily disable the timer elapsed event so that we don't have event re-entrance. 
    If Me._Timer.Enabled = True Then Me._Timer.Enabled = False 

    ' Do Work. 

    ' Re-enable the timer elapsed event. 
    _Timer.Enabled = True 

End Sub 

End Class 
+0

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

ответ

2

Ваш код включения/отключения таймера имеет тонкую ошибку. У вас есть:

If Me._Timer.Enabled = True Then Me._Timer.Enabled = False 

' Do work 

' Re-enable the timer 
_Timer.Enabled = True 

Так что если таймер отключен при записи, ваш код по-прежнему выполняется. Конечно, вы не должны получать несколько вызовов, но ваша условная проверка там практически бесполезна.

Лучший способ сделать это - инициализировать ваш таймер с помощью AutoReset, установленного на False. Это делает таймер тиком только один раз. Затем, в конце обработчика событий, снова вызовите Start, чтобы перезапустить таймер. Таким образом, вы не сможете получить несколько одновременных вызовов обработчику.

System.Timers.Timer имеет неудачное свойство давя исключений, как указано в документации:

компонентов уловов

таймер и подавляет все исключения, обработчики событий для Прошедшее событие.

Так что, если ваш обработчик событий выдает исключение, вы никогда не узнаете об этом. За исключением того, что таймер не будет повторно включен. Поэтому вам нужно написать:

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

+0

Привет, Джим. Спасибо за советы по использованию обработки AutoReset и Exception. Очень полезно. Вы указали ошибку, потому что я фактически запускаю метод один раз без таймера, запускающего его, и в этот момент таймер не работает. Каждая последующая запись включает таймер. – Rossco

1

Маршалинский звонок от рабочего потока до специфический другой поток нетривиальный. Это может поддержать только определенные типы потоков. Основной поток в Winforms или WPF-приложении квалифицируется, они особенные, потому что у них есть диспетчерский цикл. Универсальное решение для producer-consumer problem.

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

Эта инфраструктура полностью отсутствует в службе. В нем даже нет понятия «основной поток». Он не нуждается в этом, нет пользовательского интерфейса. Поэтому у вас не должно быть необходимости маршалировать вызов, просто пусть ваш обработчик события Elapsed выполнит эту работу.

Обратите внимание, что вам необходимо позаботиться о некоторых деталях, чтобы безопасный обработчик события «Истек».Вы должны установить для свойства AutoReset таймера значение False, чтобы гарантировать, что обработчик события не будет вызываться снова, пока он все еще занят. Это редко заканчивается. Вы уже сделали это сами, но лучше использовать AutoReset. И вы всегда должны использовать Try/Catch для исключения исключений. У этого класса таймеров есть неприятная привычка к глотанию исключений без диагностики, ваше обслуживание перестанет работать, и вы не поймете, почему. Запишите исключение в предложении Catch, чтобы вы знали, почему он остановился.

+0

Спасибо Хансу, очень полезные комментарии. Вначале я не пытался писать многопоточное приложение и обнаружил только отладки, что событие Timer.Elapsed встречается в другом потоке! Полезно знать, что безопасно работать в отдельных потоках, пока я не получаю повторный вход. – Rossco