【问题标题】:How do I resolve process hanging on CoUnitialize()?如何解决挂在 CoUnitialize() 上的进程?
【发布时间】:2011-04-18 18:29:20
【问题描述】:

我有一个本机 Visual C++ NT 服务。当服务启动时,它的线程调用CoInitialize() 将线程附加到一个 STA - 服务线程通过 COM 接口使用 MSXML。

当服务接收到SERVICE_CONTROL_STOP 时,它会在消息队列中发布一条消息,然后检索该消息并调用OnStop() 处理程序。处理程序清理东西并调用CoUnitialize()。大多数情况下它都可以正常工作,但偶尔后一个调用会挂起。我无法稳定地重现这种行为。

我google了一会儿,发现了以下可能的解释:

  1. 未能释放所有拥有的 COM 对象
  2. repeatedly calling CoInitializeEx()/CoUnitialize() for attaching to MTA
  3. failing to dispatch messaged in STA threads

第一个不太可能 - 使用 MSXML 的代码经过了很好的测试和分析,它使用智能指针来控制对象的生命周期,因此泄漏对象的可能性很小。

第二个原因看起来不太可能。我附加到 STA 并且不会重复调用这些函数。

第三个看起来或多或少的可能性。当线程正在处理消息时,它不再运行消息循环 - 它已经在循环内。我想这可能是原因。

后者是否可能是导致此问题的原因?我还应该考虑哪些其他原因?如何轻松解决这个问题?

【问题讨论】:

    标签: windows visual-c++ winapi com windows-services


    【解决方案1】:

    不要在处理 SCM 消息的线程中做任何有后果的事情,这是在一个奇怪的魔法上下文中 - 您必须尽快回答 SCM 的请求,而不采取任何阻塞操作。通过 STOP_PENDING 告诉它您需要额外的时间,排队另一个线程进行真正的清理,然后立即完成 SCM 消息。

    至于 CoUninitialize,只需附加 WinDbg 并转储所有线程 - 死锁很容易诊断(可能无法修复!),您已经在堆栈中找到了犯罪的所有各方。

    【讨论】:

    • 我想你明白我不喜欢一开始就引入一个单独的线程的想法。我也不能在另一个线程上调用 CoUninitialize() - 我必须在最初调用 CoInitialize() 的线程上调用它。
    • 是的,我明白了你的想法。只为处理 SCM 请求而引入一个单独的线程会使程序变得非常复杂。关键是我什至不知道它是否能解决问题。
    • 我会接受这个答案,因为它提出了正确的建议 - 使用调试器并仔细诊断情况。我的回答显示了我发现的所有细节。谢谢。
    【解决方案2】:

    经过非常仔细的分析并使用 Visual Studio 调试器(感谢 user Pall Bettspointing out 获取证据很重要)检查所有活动线程后,我发现进程挂起不是在调用 CoUninitialize(),而是在在CoUninitialize() 之前从我们的程序代码调用的RpcServerUnregisterIf() 函数。这是一个序列图:

    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 处理程序等待主线程处理请求。这是一个普通的旧僵局。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-18
      • 1970-01-01
      • 1970-01-01
      • 2021-06-04
      • 1970-01-01
      相关资源
      最近更新 更多