IUnknown.Release标准实现竞态条件?

下面是一个标准的(不是推荐的)实现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,可能会发生竞争情况:

  • 说有一个参考左边
  • 线程1通过调用Release释放它
  • 它会在delete this之前运行并停止
  • 线程2被调度并且获得对该对象的新引用,例如通过调用QueryInterfaceAddRef
  • 线程1继续执行并运行delete this
  • 线程2留下一个无效的对象

对于我来说,确保一致性的唯一方法就是创build一个标志,比如删除 ,locking整个关键部分,即除返回外的所有Release方法,并将标志设置为true

并在AddRefQueryInterface方法中检查此标志,如果设置了,则拒绝请求新的引用。

我错过了什么?

提前致谢。

线程2被调度并且获得对该对象的新引用,例如通过调用QueryInterface或AddRef

只有在已经有对IUnknown的引用或者由对象实现的其他接口之一时才能这样做。 为此之前的AddRef()调用。 因此,另一个线程的Release调用不会将引用计数减少到小于1的值。

要正确编写代码,必须将ulRefCount与0比较,而不是m_cRef。

代码中存在竞争条件,但不是您的示例中的竞争条件。 Release()这个实现有一个竞争条件,可能导致未定义的行为(通常是“双免费”)。 考虑:

  1. 线程1和线程2具有对象的引用( m_cRef == 2)
  2. 线程1调用Release()并在运行InterlockedDecrement()m_cRef == 2)后立即中断
  3. 线程2调用Release()并运行完成,所以m_cRef == 0,所以它调用delete this
  4. 线程1恢复在行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的这个实现工作?