Как накачать сообщение для потоков COM STA в C #? - PullRequest
5 голосов
/ 21 июля 2011

У меня есть основной поток STA, который вызывает много методов для COM-объекта, и дополнительный поток STA, который также много работает с одним и тем же объектом. Я хочу, чтобы основной поток и дополнительный поток работали параллельно (т.е. я ожидаю чересстрочный вывод из основного и дополнительного). Я знаю, что мне нужно время от времени качать сообщения в главном потоке - вызов Get / Translate / DispatchMessage в C ++ поможет.

Но у меня проблемы с получением той же стратегии, работающей в C #. Сначала я использовал CurrentThread.Join () в основном потоке, чтобы передать управление второму потоку. Это не сработало. Затем я обратился к Application.DoEvents () - я вызывал его в главном потоке всякий раз, когда хотел запустить второй поток. В результате второй поток быстро захватывает элемент управления и не отпускает его - основной поток не может продолжаться, пока второй поток не будет завершен.

Я читаю документы, в которых говорится, что Application.DoEvents () будет обрабатывать ВСЕ ожидающие события - в то время как GetMessage () получает только одно сообщение.

Как правильно поступить? Есть ли в C # эквивалент Get / Translate / DispatchMessage?

Спасибо

ОБНОВЛЕНИЕ: Второй поток работает слишком быстро, отправляя большое количество сообщений COM-вызовов в основной поток STA. Я просто добавил задержки во втором потоке, чтобы замедлить его. Теперь два потока в основном работают параллельно. Но я все еще хотел бы знать, существует ли C # эквивалент GetMessage / TranslateMessage / DispatchMessage.

Ответы [ 2 ]

7 голосов
/ 21 июля 2011

Ваш исходный код C ++ нарушил договор STA.В котором указывается, что указатель интерфейса должен быть маршалирован из одного потока в другой, чтобы все вызовы объекта выполнялись только из одного потока.Это является жестким требованием для однопоточных COM-серверов, и в противном случае возникает риск типичного страдания, связанного с вызовами make из нескольких потоков в коде, который не является поточно-ориентированным.Использование двух потоков STA не освобождает вас от этого требования, объект принадлежит только потоку, который его создал.2-й поток - это просто еще один поток, и вызовы из него не могут быть сделаны безопасно, поскольку сервер не поддерживает многопоточность.

Вам как-то это не понравилось в коде C ++, трудно представить, что его не былоглюк сейчас и потом.В противном случае COM не может принудительно исполнить контракт STA на внутрипроцессных COM-серверах, только на внепроцессных серверах.Нарушение контракта порождает RPC_E_WRONG_THREAD в этом случае.

В любом случае, вам больше не сходит с рук это в программе на C #.CLR автоматически маршалирует указатель интерфейса для вас.Вызовы, которые вы делаете во 2-м потоке, будут маршалированы в поток STA, которому принадлежит объект.Чередование продолжается, но вызовы 2-го потока могут быть доставлены только тогда, когда первый поток бездействует и снова входит в цикл обработки сообщений.Обходного пути для этого не существует, обработка указателя интерфейса в CLR осуществляется строго по правилам.

Это будет иметь много последствий для вашего кода, я думаю, самое большое из них - то, что 2-й поток действительно больше ничего не выполняет.Параллелизма нет, все вызовы объекта строго сериализуются.И потокобезопасен.Вам, вероятно, будет лучше просто делать все звонки из одного потока, чтобы вам не пришлось танцевать вокруг значительного риска тупика.Создание правильной прокачки сообщений в качестве бонуса менее критично.Если 2-й поток выполняет другую критическую работу, то может быть полезно использование поддержки COM для однопоточного кода.

3 голосов
/ 21 июля 2011

Что касается Get / Translate / Dispatch в .Net, вы должны иметь возможность вызывать Application.Run или Dispatcher.Run () (в зависимости от того, используете ли вы winformsили wpf) в вашей ветке помощников.Это накачает цикл сообщений в этом потоке, вызывая Get / Trans / Dispatch для вас.Если вы не согласны с этой идеей, то вы можете P / Invoke звонки Win32.

В то время как .Net заставит почти все работать на вас, как в ответе hans ', на практике я'мы обнаружили, что сообщение, поступающее от COM-объекта, может вызвать заикание вашего потока пользовательского интерфейса, поскольку базовая функция DispatchMessage (), кажется, занимает много времени (определенно, дольше, чем я ожидал, или могла бы учитывать).Мы изменили наше решение для создания COM-объекта во вспомогательном потоке и явно распределяли вызовы из потока пользовательского интерфейса.

...