下面是一个标准的(不是推荐的)实现IUnknown COM接口的Release方法(直接取自MSDN)的方法:
ULONG CMyMAPIObject::Release() { // Decrement the object's internal counter. ULONG ulRefCount = InterlockedDecrement(m_cRef); if (0 == m_cRef) { delete this; } return ulRefCount; }
我想知道如果公寓模型不是STA,可能会发生竞争情况:
delete this
之前运行并停止 delete this
对于我来说,确保一致性的唯一方法就是创build一个标志,比如删除 ,locking整个关键部分,即除返回外的所有Release方法,并将标志设置为true 。
并在AddRef和QueryInterface方法中检查此标志,如果设置了,则拒绝请求新的引用。
我错过了什么?
提前致谢。
线程2被调度并且获得对该对象的新引用,例如通过调用QueryInterface或AddRef
只有在已经有对IUnknown的引用或者由对象实现的其他接口之一时才能这样做。 为此之前的AddRef()调用。 因此,另一个线程的Release调用不会将引用计数减少到小于1的值。
要正确编写代码,必须将ulRefCount与0比较,而不是m_cRef。
代码中存在竞争条件,但不是您的示例中的竞争条件。 Release()
这个实现有一个竞争条件,可能导致未定义的行为(通常是“双免费”)。 考虑:
m_cRef
== 2) Release()
并在运行InterlockedDecrement()
( m_cRef
== 2)后立即中断 Release()
并运行完成,所以m_cRef
== 0,所以它调用delete this
。 if (0 == m_cRef)
和m_cRef
== 0,所以它调用再次delete this
,导致未定义的行为(通常是一个“双免费的”错误)。 正确的实现是:
ULONG CMyMAPIObject::Release() { // Decrement the object's internal counter. ULONG ulRefCount = InterlockedDecrement(m_cRef); if (0 == ulRefCount) //<<<< THIS FIXES THE PROBLEM { delete this; } return ulRefCount; }
if检查需要在本地ulRefCount
变量上。 因为InterlockedDecrement()
返回它递减的值,所以在线程2的调用中ulRefCount
只会是零,所以delete this
只会在线程2上调用。
if (0 == m_cRef)
正在访问没有锁定的共享状态,并且不安全。
请参阅此问题的答案: 为什么COM IUnknown :: Release的这个实现工作?