是否IUnknown::QueryInterface()增加引用计数?



如果我有一个IUnknown *ptr,我是否需要在我通过ptr->QueryInterface()获得的每个接口上调用Release()除了当我完成ptr时调用ptr->Release() ?

我曾经认为答案是"是",但是MSDN的这句话让我困惑:

偶尔你可能需要获得一个对象的弱引用(也就是说,你可能希望获得一个指向它的一个接口的指针而不增加引用计数),但是通过调用QueryInterfaceRelease 来实现这一点是不可接受的。

我不明白为什么这是有问题的——如果我调用ptr->QueryInterface(),然后在结果指针上调用Release,对象上的引用计数不应该仍然是正的吗?这是如何导致无效的指针?

文档正确。您还需要遵循引用计数规则——除了在创建对象之后,还包括在从QueryInterface获得的接口上调用Release

为了澄清为什么你不能用Release做弱指针——在调用QueryInterfaceRelease之后存在一个竞争条件。

  • Thread1创建对象-引用计数1
  • Thread2调用QueryInterface进行弱引用-引用计数2
  • Thread1释放对象-引用计数1
  • Thread2调用Release进行弱引用-引用计数为0。对象被销毁。
  • Thread2试图使用object - error.

这个警告是为了防止上面的问题——可能一些程序员认为他们可以"调用ptr->QueryInterface(),然后在结果指针上调用Release",然后使用对象…

IUnknown:: QueryInterface方法

获取指向对象上支持的接口的指针。

该方法在返回的指针上调用IUnknown::AddRef。

直接来自IUnknown::QueryInterface reference athttp://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

标题不是唯一的场景;我想说的是,线程实际上根本不是主要的场景:这些COM规则可以追溯到Win16,在抢占式多线程被添加到Windows之前。

关键问题是,就COM而言,引用计数是每个接口,而不是每个对象。COM实现可以通过每个对象来实现引用计数——这可能是c++中最简单的实现方式,尤其是当COM对象映射到单个c++对象时——但这只不过是一个实现细节,COM客户端代码不能依赖于这种情况。

有许多COM对象可以根据需要动态地生成接口,然后在不再需要它们时销毁它们。在这些情况下,如果您调用QI来获取这些接口之一,一旦您调用Release,该接口的内存就会被重新分配,因此使用它可能会导致故障/崩溃等。

一般来说,你必须把任何对->Release()的调用看作是潜在地释放指针后面的内存。

(另外请注意,COM一开始并没有弱引用的概念:有计数(强)引用,仅此而已)

避免弱引用的建议并不能解决争用问题。

T1 operator new, create object, references: 1
T1     passes interface object reference to T2, thinking it can "share" ownership
T1     suspends
T2     resumes
T2 QueryInterface
T2     suspends before InterlockedIncrement, references: 1
T1     resumes
T1 Calls Release
T1     suspends between InterlockedDecrement and operator delete, references: 0
T2     resumes, InterlockedIncrement occurs, references 1
T2     suspends
T1     resumes, operator delete executes, references 1 !!!
T1     suspends
T2     resumes
T2 Any reference to the interface is now invalid since it has been deleted with reference count 1.

这可以在COM服务器中解决。但是,COM客户端不应该依赖于阻止这种竞争条件的服务器。因此,COM客户端绝对不能在线程之间共享接口对象。唯一应该被允许访问接口对象的线程,是当前"拥有"接口对象的一个线程。

T1不应该调用Release。是的,它可以在将接口对象传递给T2之前调用AddRef。但这可能解决不了竞争,只会把它转移到别的地方。最佳实践是始终保持一个接口-对象,一个所有者的概念。

如果COM服务器希望支持两个(或更多)接口可以引用一些共享的服务器内部状态的概念,那么COM服务器应该通过提供CreateCopyOfInstance方法来发布契约,并在内部管理争用。当然,也有服务器处理这种"扇形输出"的例子。看看Microsoft的持久存储接口。在那里,接口不会"扇出"…每个接口应该由单个用户(线程/进程/其他)拥有,"扇出"由服务器在内部管理,并向COM客户端提供方法来控制争用问题的某些方面。因此,微软的COM服务器必须将竞态条件作为它们与客户签订的合同的一部分。

相关内容

  • 没有找到相关文章

最新更新