Dispatcher.Invoke «зависает» во время асинхронного чтения в Windows Service - PullRequest
3 голосов
/ 15 сентября 2011

Я создал службу Windows на основе класса ServiceBase.В этом сервисе я создал экземпляр NamedPipeClientStream (m_Stream).После подключения этого потока я запускаю асинхронное чтение, используя метод BeginRead ():

m_Stream.BeginRead( m_ReadBuffer, 0, 2, ReadAsyncCallback, m_ReadInfo );

В подпрограмме обратного вызова ReadAsyncCallback, которая действительно вызывается, я вызываю EndRead () для потока (который дает мне номерпрочитанных байтов, в данном случае 2).Далее я хочу сообщить исходной теме, что чтение завершено.Для этого я использую метод Dispatcher.Invoke:

m_Dispatcher.Invoke( new ReadDelegate( this.OnRead ), bytesRead);

(m_Dispatcher был создан в исходном потоке с использованием System.Windows.Threading.Dispatcher.CurrentDispatcher.)

На данный момент я ожидалметод OnRead для вызова в исходном потоке, но это не так.Метод Invoke () не возвращается, кажется, «зависает».

Я надеюсь, что кто-то может помочь мне с этим.Пожалуйста, дайте мне знать, если вам нужна дополнительная информация, я постараюсь дать ее вам как можно скорее.

Привет, Ричард

Ответы [ 2 ]

3 голосов
/ 15 сентября 2011

Для System.Windows.Threading.Dispatcher требуется правильно настроенный SynchronizationContext, чтобы он работал так, как вы обычно ожидаете.Когда в контексте приложения WPF контекст синхронизации создается для вас автоматически, однако в вашей службе Windows этого не происходит, и поэтому вы видите зависание.

Кроме того, помимо контекста синхронизации, так как я считаю,Dispatcher работает аналогично Control.Invoke или BackgroundWorker в Windows Forms, ваш основной поток службы Windows должен прокачивать цикл сообщений, чтобы вы могли вводить в него свой вызов.

Я написал блог о том, как класс BackgroundWorker реагирует по-разному в зависимости от контекста, в котором он запускается (Windows Forms, Console или Windows Service), что может показаться вам интересным, поскольку механизм, используемый этимКласс похож на WPF Dispatcher.

Внутри BackgroundWorker

Наконец, для более глубокого погружения в работу контекстов синхронизации вы должны прочитать:

Это все о SynchronizationContext

2 голосов
/ 15 сентября 2011

Поток, вызвавший CurrentDispatcher, вероятно, по какой-то причине не качает сообщения.Наиболее вероятная причина в том, что у него нет механизма прокачки сообщений.Для правильной работы Invoke целевой поток должен быть специально разработан для приема инъекций делегатов.Обычно это достигается тем, что целевой поток вращается в бесконечном цикле, ожидая появления сообщений в очереди.Затем другой поток отправит специальное сообщение, запрашивающее выполнение делегата.Все это настраивается автоматически в потоке пользовательского интерфейса Windows Forms или приложений WPF.Он не будет существовать в приложении службы Windows, пока вы не запустите его вручную.

Я бы не стал пытаться использовать этот метод маршалинга делегатов (или любой метод, который синхронно внедряет делегат в другой поток) в любом случае.Причина в том, что это приведет к тому, что асинхронный обратный вызов ввода-вывода, который выполняется в потоке ThreadPool или в потоке порта завершения ввода-вывода, будет блокироваться до завершения этого делегированного маршалла.Вы не хотите связывать IO таким способом.

Вместо этого вы должны опубликовать данные, считываемые из потока, в общую структуру данных, такую ​​как очередь или список, и затем выбрать исходный потокэто на определенном интервале.Если ожидается, что исходный поток будет ожидать чтения данных из потока, вы можете настроить шаблон производитель-потребитель.Это довольно легко с BlockingCollection.Исходный поток будет вызывать Take, который будет блокироваться до тех пор, пока не прибудет элемент, и обратный вызов IO опубликует данные, вызвав Add.

Есть другие приемлемые способы, которые могут быть обработаны, но вызов Invokeвероятно, не один из них.

...