以下是实施 IUnknown COM 的 发布 方法的标准(不推荐)方式接口(直接取自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
对我来说,确保一致性的唯一方法是创建一个标志,比如说已删除,锁定整个关键部分,即除了返回之外的所有 Release 方法,并将标志设置为 true 。
在 AddRef 和 QueryInterface 方法中检查此标志,如果已设置,则拒绝新引用请求。
我错过了什么?
提前致谢。
答案 0 :(得分:6)
调度线程2并获得对该对象的新引用,例如通过调用QueryInterface或AddRef
只有在已经有对IUnknown或对象实现的其他接口之一的引用时才能执行此操作。之前进行了AddRef()调用。因此,引用计数可以永远通过另一个线程的Release调用减少到小于1的值。
正确编写代码,必须将ulRefCount与0进行比较,而不是m_cRef。
答案 1 :(得分:4)
代码中存在竞争条件,但它不是您示例中的竞争条件。 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()
返回它递减的值,所以ulRefCount
在线程2的调用上只会为零,所以delete this
只会在线程2上调用。
if (0 == m_cRef)
正在访问没有锁定的共享状态,并且不安全。
同时查看此问题的答案:Why does this implementation of COM IUnknown::Release work?