gpt4 book ai didi

c++ - COM 初始化和清理是否适合函数级粒度?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:30:13 27 4
gpt4 key购买 nike

考虑编写一个可重用的自定义函数,在其函数体内创建 COM 对象并调用某些 COM 接口(interface)的方法。为了使其正常工作,必须调用 CoInitializeEx 和匹配的 CoUninitialize API。

在函数体内部调用那些 COM 初始化和清理 API 会向调用者隐藏 COM 实现细节,同时也会减轻调用者的负担。

但是在函数体内调用 CoInitializeEx 和匹配的 CoUninitialize 是否被认为是一种好的编码习惯?

在函数粒度级别调用那些 COM 初始化/清理函数是否意味着每个函数调用的开销太大?

这种设计还有其他缺点吗?

最佳答案

这是一种糟糕的做法,而且根本上是错误的。重要的是第二个参数 (dwCoInit) 的值。它必须是 COINIT_APARTMENTTHREADED,通常缩写为 STA,或 COINIT_MULTITHREADED (MTA)。这是你做出的 promise ,誓死不渝的风格。如果你违背了 promise ,那么程序就会死掉。通常是由于死锁、未获得预期事件或性能慢得令人无法接受。

当您选择 STA 时,您就可以保证该线程运行良好并且可以支持非线程安全的 COM 组件。实现这个 promise 需要线程泵出一个消息循环并且从不阻塞。例如,支持 GUI 的线程的常见行为。绝大多数 COM 组件都不是线程安全的。

当您选择 MTA 时,您根本就没有 promise 任何支持。该组件现在必须自生自灭以保持自身线程安全。通常通过让 COM 基础结构自己创建一个线程来为组件提供一个安全的家来自动完成。您需要注意的进一步细节是编码(marshal)接口(interface)指针,需要 CoMarshalInterThreadInterfaceInStream() 辅助函数或更方便的 IGlobalInterfaceTable 接口(interface)。这确保创建一个代理来处理所需的线程上下文切换。

MTA 听起来很方便,但并非没有后果,一个简单的属性 getter 调用可能会花费多达 x10000 倍的时间。线程上下文切换带来的开销以及跨堆栈帧复制任何参数和返回值的需要。编码(marshal)接口(interface)指针很容易失败,COM 组件的作者通常不提供必要的代理/ stub ,或者他们故意省略它,因为复制数据太困难或太昂贵。

关键是图书馆永远无法在 STA 和 MTA 之间做出选择。它不知道关于线程的 beans,它没有创建那个线程。并且不可能知道线程是否泵出消息循环或 block 。这些代码完全超出了图书馆的范围。否则 COM 基础结构也需要知道这一点的确切原因,它同样无法对线程做出假设。

选择必须由创建和初始化线程的代码做出,通常是应用程序本身。除非库为了使调用安全而创建线程。但是随之而来的结果是代码总是很慢。您通过返回不可避免的 CO_E_NOTINITIALIZED 错误代码来提醒库的调用者他没有正确处理。

Fwiw,这是您在 .NET Framework 中看到的东西。 CLR 总是在线程可以执行任何托管代码之前调用 CoInitializeEx()。仍然是应用程序的程序员必须做出的选择,或者更典型的是项目模板,通过 Main() 上的 [STAThread] 属性或工作线程的 Thread.SetApartmentState() 调用来完成。

关于c++ - COM 初始化和清理是否适合函数级粒度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40936774/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com