【问题标题】:Can I access the interface of a COM object from different threads?我可以从不同的线程访问 COM 对象的接口吗?
【发布时间】:2014-03-05 16:43:18
【问题描述】:

我可以在这样的服务的主工作线程中访问IAudioEndpointVolume 接口(省略错误检查):

CoInitialize(NULL);
CoCreateGuid(&g_guidMyContext);
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, (void**)&m_pEnumerator);

IMMDevice *pEndpoint = NULL;
m_pEnumerator->GetDevice(&deviceID[0], &pEndpoint);

IAudioEndpointVolume *pAudioVol = NULL;
pEndpoint->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pAudioVol);

CAudioEndpointVolumeCallback *pVolumeCallback = new CAudioEndpointVolumeCallback(pAudioVol, 100, TRUE, m_debugLog, m_logMutex);

然后CAudioEndpointVolumeCallback 类将IAudioEndpointVolume 指针存储在一个成员变量中,并在其构造函数中调用RegisterControlChangeNotify(this)

这个类也可以启动一个线程来在短时间内将音量从一个值平滑地更改为另一个值。所以最后我从不同的线程调用IAudioEndpointVolume 接口。一般来说,所有这些都按预期工作,但在某些极端情况下我遇到了一些奇怪的行为。再次检查代码时,我偶然发现了 MSDN 文档中关于线程的 cmets(我之前没有任何 COM 对象的经验)并想知道我所做的是否正确。

那么,使用上面的代码从不同线程调用IAudioEndpointVolume 接口是否安全?我是否必须使用互斥锁来保护这些调用?

我有点困惑,不确定我是否真的理解behavior of the COM Threading Models

【问题讨论】:

  • 这不太可能与线程有关,因为您从获得接口指针的同一个线程进行调用。这很可能与使用服务有很大关系。你应该引用你的previous question about this
  • 我不会从获得指针的同一个线程进行(所有)调用!我对我的代码进行了一些关于线程安全的更改,这解决了我在使用此服务时遇到的其他一些问题。因此,这为我指明了导致我发布此问题的方向。稍后我会从另一个问题中尝试您的建议。实际上,我怀疑它是否会解决那里的 cmets 中发布的问题。反正我会试试的。但是,请不要将其视为个人 ;)

标签: c++ multithreading winapi com


【解决方案1】:

CoInitialize(NULL) 初始化单线程单元 (STA),并且您拥有的接口指针仅 [应该] 仅在此线程上有效。您可以通过GIT 将它们传递给另一个线程,或者通过此线程上的CoMarshalInterThreadInterfaceInStream 并在目标线程上使用CoGetInterfaceAndReleaseStream 将其“复制”回来,您将在其中使用它。

根据相关接口指针背后的实现,您最终可能会获得相同的指针(在这种情况下,整个技巧将等同于简单地在另一个线程上使用该指针),但是这在一般情况下并不安全。

另一种选择是使用 MTA(与 STA 相对),然后您可以直接在所有/任何 MTA 线程之间传递指针。

【讨论】:

  • 那么如果我使用CoInitializeEx(NULL, COINIT_MULTITHREADED) 并使用互斥锁保护对接口的访问,我是否安全?但是我必须在两个/所有线程中都调用CoInitializeEx
  • 是的,你很安全。是的,在所有线程上。但请注意,您通常无法灵活选择 STA/MTA。如果线程已被调用者初始化为 STA,则您没有重新初始化的选项。在大多数情况下,问题是这样表述的:“我的代码在 STA (MTA) 线程上运行,如何正确使用工作线程上的指针”。
  • @RomanR。 IUIAutomation 接口也有类似的情况。我在 MTA 线程中创建它,但随后指针被传递并被其他线程使用。这些线程没有调用 coinitialize 并在没有同步的情况下对接口进行调用。一切似乎都很好,但它安全吗?其他线程怎么可能以这种方式使用接口?谢谢
  • @lorenzolightsgdwarf 指针的安全使用是在单元内传递(也就是说,如果您知道它是 MTA 或者您自己将其初始化为 MTA,则传递给另一个线程)。如果您不这样做,由于所谓的“隐式 MTA”,它可能仍然有效 - 如果您至少有一个 MTA 线程,那么其他没有 COM 初始化的线程将被 COM 视为隐式初始化为 MTA。有时,特定的 COM 服务器实现也使得调用错误的公寓不会产生任何后果,因此您也可以看到由于这个原因而工作。
  • 为在 MTA 中工作而设计的 COM 服务器(以及 API 及其各种对象)通常被假定为从多个线程中使用,因此它们负责为并发调用做好准备并在其端添加同步。因此,您通常可以安全地从 MTA 中的多个线程调用 COM 对象。可能有一些特定的原因,或者您预先知道 API 有问题或有特殊需求,在这种情况下,您可能想尝试在外部进行一些同步。
猜你喜欢
  • 1970-01-01
  • 2013-11-23
  • 2014-01-06
  • 1970-01-01
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-09
相关资源
最近更新 更多