VB.NET Прервать асинхронный вызов метода после тайм-аута. - PullRequest
1 голос
/ 06 октября 2010

VB.NET 2010, .NET 4

Привет всем,

У меня есть объект System.Timers.Timer, который выполняет некоторую работу над своим прошедшим событием:

Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed
    MasterTimer.Enabled = False
    '...work...
    MasterTimer.Enabled = True
End Sub

Моя проблема в том, что работа, которую он выполняет, иногда застревает. Частью работы является последовательная связь, поэтому она может застрять в ожидании ответа от чего-либо. Я уже немного изменил свой код последовательной связи, чтобы, надеюсь, решить проблему. Тем не менее, этот таймер в основном является пульсом приложения управления производством, и он очень плох, если его остановить по какой-либо причине. Я подумал, что было бы неплохо установить отказоустойчивый тайм-аут, чтобы, если «работа» заняла слишком много времени, таймер мог снова включить себя и повторить попытку. Я думал о чем-то вроде этого:

Переместите работу в подпрограмму и создайте делегата:

Private Delegate Sub WorkDelegate()
Private Sub Work()
   '...work...
End Sub

Вызовите работу, вызвав делегата, а затем используйте WaitOne (timeout) в IAsyncResult, чтобы указать время ожидания:

Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed
    MasterTimer.Enabled = False
    Dim workDel as New WorkDelegate(AddressOf Work)
    Dim result as IAsyncResult = workDel.BeginInvoke
    result.AsyncWaitHandle.WaitOne(CInt(MasterTimer.Interval))
    MasterTimer.Enabled = True
End Sub

Но мой вопрос таков: не вызовет ли это проблемы, если Work () действительно где-то застрянет? В том, что он повторно введет подпрограмму, которая уже запущена? Есть ли способ прервать работу Work (), если она не завершилась после истечения времени ожидания? Другими словами, просто прекратите выполнение Work (), если result.IsCompleted False после WaitOne?

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

Заранее большое спасибо!

Я бы хотел добавить кое-что:

Несмотря на то, что я собираюсь сделать некоторые изменения в соответствии с рекомендациями Ганса, я уже запланировал день тестирования, чтобы попытаться изолировать источник этой ошибки. До сих пор это происходило только при запуске скомпилированного приложения. Я потратил сегодня (и некоторые вчера) попытки воспроизвести стоп-кадр во время работы в режиме отладки, чтобы, возможно, я мог добавить некоторые точки останова и выяснить, что происходит. Пока что программа не зависла в режиме отладки. Мне просто интересно, есть ли что-то другое в среде отладки, которая могла бы объяснить это. Может быть, мне просто «везет», но я запустил его, вероятно, в три раза больше средней продолжительности, после которой программа зависла при запуске исполняемого файла ... Опять же, я довольно невежественен, но есть ли что-нибудь уникальна среда отладки, которая могла бы объяснить это? Я буду обновлять снова, если он зависает.

1 Ответ

2 голосов
/ 07 октября 2010

Это не так в самой первой строке кода.Класс System.Timers.Timer довольно странный, абсолютно нет гарантии, что вызов Stop () предотвратит другой вызов.Таймер использует ThreadPool.QueueUserWorkItem для вызова обработчика события Elapsed.Если пул потоков занят, это может в конечном итоге поставить в очередь несколько вызовов, ожидающих получения разрешения от планировщика TP для запуска.Остановка таймера не мешает запуску ожидающих потоков.Без использования блокировки эти потоки будут сильно наезжать друг на друга и портить ваше состояние связи.

Безопасным является System.Threading.Timer с нулевым периодом, поэтому вы получите только один обратный вызов.Перезарядите таймер с помощью метода Change ().

Вызов метода делегата BeginInvoke () и последующая блокировка его завершения не имеют смысла.Просто позвоните в Invoke ().Спасает вас от записи другого потока.

Да, если метод «Работа» никогда не возвращается, у вас есть проблема.Неразрешимый.

Многие из этих страданий могут исчезнуть, если вы будете избегать использования опроса, чтобы узнать, есть ли что-нибудь доступное на последовательном порту.Пусть он скажет вам, что происходит что-то стоящее.Он вызывает событие DataReceived в потоке потоков, когда в приемном буфере есть хотя бы один байт.Использование свойства WriteTimeout также является отличным способом избежать застревания, когда что-то не так с протоколом связи или устройством.Также хорошо работает выделение одного потока и блокирование вызовов чтения.

...