COM引用计数-线程安全

3
如果我有一个COM对象,AddRef()和Release()方法是否需要线程安全-也就是说,我是否必须使用原子操作来处理我的引用计数?

同样的规则适用于其他方法。STA COM对象不需要关心线程安全性,MTA COM对象必须要关心(InterlockedXxx是最简单的方法)。 - Roman R.
4个回答

3
我认为答案是否定的。这不是必需的。如果您希望您的COM对象是线程安全的,则这些对象应该是线程安全的。否则,它们不必是。
例如,如果您在此处查看:组件对象模型规则中没有提到它作为要求。 此外,在COM程序员烹饪书(构建COM组件)中,您可以看到一个没有线程安全引用计数的示例对象。
Microsoft 代码片段:
ULONG COutside::AddRef (void)
{
    return ++ m_cRef;
}

实际上,大多数实现都会这样做,否则COM对象就不会是线程安全的。如果你知道该对象只会在一个线程中使用,那么我认为这是一个允许的优化。并不是所有的COM对象都是线程安全的,我曾经遇到过一些不安全的情况。
为了解决COM对象可能或可能不是线程安全的问题,COM提供了不同的“单元”,在其中创建COM对象。在单线程单元中,只有一个线程可以访问该单元内的对象,而在多线程单元中,对象可以在多个线程之间共享。引用自Understanding and Using COM Threading Models
“虽然多线程单元(有时称为自由线程单元)是一个更简单的模型,但它们更难以开发,因为开发人员必须为对象实现线程同步,这是一个非常不平凡的任务。”

你的回答虽然不是完全没有用,但却非常不完整,存在潜在的危险。你至少应该提到自由线程访问的可能性(链接页面出于我无法理解的原因也没有提到)。 - sehe
这仍然是正确的。程序员不使用线程不安全的对象在错误的区域中,这取决于程序员。这很危险吗?是的。但这不是我制造的。我会在我的答案中添加关于此的注释。 - Guy Sirton
说实话:我认为大多数COM对象都不是自由线程的(事实上,在过去,我有一种明显的印象,即微软大师们将自由线程的COM对象视为本世纪最大的技术噱头);COM的文化是隐藏线程的复杂性并自动跨公寓边界进行编组。然而,既然问题是开放式的并且特别提到了线程安全性,你至少应该提到自由线程的情况 :) 现在你已经做到了,致敬。 - sehe
@sehe:这也是我的经验。 我从字面上回答了这个问题,无论在COM中是否需要这些线程安全性。 也许我需要添加更多背景 - 还在适应这个SO的东西 :-) - Guy Sirton

3

如果您正在使用自由线程公寓模型,则可以使用InterlockedIncrement()和InterlockedDecrement()来处理引用计数。


1
使用InterlockedIncrement()等是一般惯用语,它是线程安全的。 - seand

1

是的,这是必需的。COM 是一个简单的二进制标准,如果您使用自由线程公寓,您将获得真正的自由线程访问。


1
这将取决于您使用的线程模型和对象类型。请参见_ATL_*_THREADED的说明。这些宏会影响“通常”类和工厂的AddRef()/Release()的线程安全性。
如果您使用了“过于宽松”的宏,您将违反线程安全要求,程序可能会出现故障。如果您选择了“过于严格”的宏,可能会失去一些性能,但通常在进行性能分析之前您不知道自己是否关心。
以下是如何选择正确的宏(以及这解释了是否必须使AddRef()/Release()线程安全)。
如果单个服务器的所有类都未指定线程模型(主STA),则不存在同时访问对象或工厂的机会,它们都可以具有非线程安全的AddRef()/Release(),并且通过指定_ATL_SINGLE_THREADED宏来实现。
否则,如果至少有一个类指定了“Apartment”模型,则需要线程安全的AddRef()/Release()来创建该对象的工厂,但是对象本身可以具有非线程安全的AddRef()/Release(),通过指定_ATL_APARTMENT_THREADED宏即可实现。此宏将使所有工厂具有线程安全的AddRef()/Release(),并且所有对象都具有非线程安全的AddRef()/Release()
最后,如果至少有一个类指定了“Both”或“Free”线程模型,则需要在该类和工厂中使AddRef()/Release()线程安全,并且您必须指定_ATL_FREE_THREADED或者不指定上述任何一个 - 这个“最紧密”的宏效果将默认启用。因此,使用ATL创建的COM对象的默认配置是所有对象 - 服务对象和工厂都具有线程安全的AddRef()/Release()
话虽如此,您并不总是需要AddRef()/Release()是线程安全的,但通常应该这样做,除非您确定可以不使用它,并且不使用它可以让您获得更好的性能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接