Как мне разрешить зависание процесса на CoUnitialize ()? - PullRequest
4 голосов
/ 29 сентября 2010

У меня есть собственная служба Visual C ++ NT. Когда служба запускается, ее поток вызывает CoInitialize(), который присоединяет поток к STA - поток службы использует MSXML через интерфейсы COM.

Когда служба получает SERVICE_CONTROL_STOP, она отправляет сообщение в очередь сообщений, затем позднее это сообщение извлекается и вызывается обработчик OnStop(). Обработчик убирает вещи и вызывает CoUnitialize(). Большую часть времени он работает нормально, но время от времени последний звонок зависает. Я не могу воспроизвести это поведение стабильно.

Я немного погуглил и нашел следующие вероятные объяснения:

  1. не удалось освободить все принадлежащие COM-объекты
  2. многократный вызов CoInitializeEx() / CoUnitialize() для подключения к MTA
  3. не удается отправить сообщение в потоках STA

Первый вариант маловероятен - код, использующий MSXML, хорошо протестирован и проанализирован, и в нем используются интеллектуальные указатели для управления временем жизни объектов, поэтому утечка объектов в действительности маловероятна.

Второй не выглядит вероятной причиной. Я присоединяюсь к STA и больше не вызываю эти функции.

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

Является ли последняя вероятной причиной этой проблемы? Какие еще причины я должен рассмотреть? Как мне легко решить эту проблему?

Ответы [ 2 ]

3 голосов
/ 30 сентября 2010

Не выполняйте ничего последствий в потоке, обрабатывающем сообщения SCM, это в странном магическом контексте - вы должны отвечать на запросы SCM как можно быстрее, не предпринимая каких-либо блокирующих действий. Скажите, что вам нужно дополнительное время через STOP_PENDING, поставьте в очередь другой поток, чтобы выполнить реальную очистку, а затем немедленно завершите сообщение SCM.

Что касается CoUninitialize, просто подключите WinDbg и сбросьте все потоки - взаимные блокировки легко диагностировать (возможно, не исправить!), У вас есть все участники преступления прямо в стеках.

1 голос
/ 25 января 2011

После очень тщательного анализа и использования отладчика Visual Studio (спасибо пользователю Pall Betts за , указывающему , что получение доказательств важно) для проверки всех активных потоков, я обнаружил, что процесс зависать не при вызове CoUninitialize(), а вместо RpcServerUnregisterIf() функции, вызываемой из нашего программного кода прямо перед CoUninitialize(). Вот схема последовательности:

WorkerThread                            RpcThread                    OuterWorld
  |----| Post "stop service" message        |                            |
  |<---|                                    |  SomeRpcServerMethod()     |
  |      Post "process rpc request"         |<---------------------------|
  |<----------------------------------------|                          waits
  |                                         |----|Wait until
  |----| Process "stop service" message     |    |request is processed
  |<---| (call OnStop())                    |    |by the worker thread
  |                                         |    |
  |----| RpcServerUnregisterIf()            |    |
  |X<--| Wait all rpc requests complete     |X<--|
  |                                         |

Приходит входящий запрос RPC, и среда выполнения RPC порождает поток для его обслуживания. Обработчик запросов помещает запрос в рабочий поток и ожидает.

Теперь лунная фаза оказывается правильной, и поэтому RpcServerUnregisterIf() выполняется параллельно с обработчиком в потоке RPC. RpcServerUnregisterIf() ожидает завершения всех входящих запросов RPC, а обработчик RPC ожидает, когда основной поток обработает запрос. Это старый тупик.

...