2012-05-09 62 views
0

在某些C++/CLI代码中,我有一个本机类,其中有一个工厂方法GetWrapper()用于创建它自己的受管.NET包装器对象。在内部,它通过GCHandle持有对其包装的弱引用。当调用GetWrapper()时,将检查GCHandle,并返回现有包装器的句柄,或者(如果它不再指向对象,因为旧包装器对象已被垃圾收集器销毁),则创建一个新包装器返回。GCHandle.IsAllocated不会将弱句柄返回false以销毁对象

// .h 
class NativeClass 
{ 
public: 
    WrapperClass^ GetWrapper(); 
private: 
    WrapperClass^ GetNewWrapper(); 
    GCHandle m_wrapperGCHandle; 
}; 

// .cpp 
WrapperClass^ NativeClass::GetWrapper() 
{ 
    if(m_wrapperGCHandle.IsAllocated) 
    { 
     try 
     { 
      WrapperClass^ wrapper = nullptr; 
      wrapper = dynamic_cast<WrapperClass^>(wrapperGCHandle.Target); 

      if(wrapper == nullptr) 
      { 
       return GetNewWrapper(); 
      } 
      else 
      { 
       return wrapper; 
      } 
     } 
     catch(System::InvalidOperationException^) 
     { 
      return GetNewWrapper(); 
     } 
    else 
    { 
     return GetNewWrapper(); 
    } 
} 

WrapperClass^ NativeClass::GetNewWrapper() 
{ 
    WrapperClass^ wrapper = gcnew WrapperClass(/*some args*/); 
    m_wrapperGCHandle = GCHandle::Alloc(wrapper, GCHandleType::Weak); 
} 

奇怪的,现在是m_wrapperGCHandle.IsAllocated总是返回true,即使包装已被垃圾收集。将MSDN tells设置为“在使用弱句柄时使用此属性来确定GCHandle是否仍然可用”。但它总是如此。如果它不可用,那么Target就是一个nullptr。

我错过了什么或者是MSDN错误吗?

+0

您的代码中存在隐式线程竞争。测试IsAllocated后,GC可能会运行正常。 –

+0

@HansPassant是的,它目前不是线程安全的 –

回答

1

我读到的MSDN doco是m_wrapprGCHandle.IsAllocated将返回true,直到调用m_wrapperGCHandle.Free - IsAllocated属性正在检查句柄的状态,而不是句柄所持有的引用的状态。

正如您所注意到的,当对象被垃圾收集时,m_wrapperGCHandle.Target为空。我使用了类似的方法来生成托管包装类,并且我总是检查Target是否为空,并在Target为空时重新生成包装对象。

此外,一个建议......它看起来好像你在你的代码中有句柄泄漏,因为你打电话GCHandle::Alloc没有相应的m_wrapperGCHandle.Free调用。尝试把呼叫Alloc在类的构造函数,并调用Free在析构函数:

NativeClass::NativeClass() 
{ 
    m_wrapperGCHandle = GCHandle::Alloc(nullptr, GCHandleType::Weak); 
} 

NativeClass::~NativeClass() 
{ 
    m_wrapperGCHandle.Free(); 
} 

那么你GetNewWrapper方法很简单:

WrapperClass^ NativeClass::GetNewWrapper() 
{ 
    m_wrapperGCHandle.Target = gcnew WrapperClass(/*some args*/); 
} 

,你可以从GetWrapper删除if(m_wrapperGCHandle.IsAllocated) - else链方法。