C ++ / CLI:防止非托管资源的托管包装上的垃圾回收

时间:2010-12-06 13:20:57

标签: c# garbage-collection c++-cli finalizer

我有一个需要在C#中使用的C ++非托管类NativeDog,所以我创建了一个包装类ManagedDog

// unmanaged C++ class
class NativeDog
{
    NativeDog(...); // constructor
    ~NativeDog(); // destructor
    ...
}

// C++/CLI wrapper class
ref class ManagedDog
{
    NativeDog* innerObject; // unmanaged, but private, won't be seen from C#
    ManagedDog(...)
    {
        innerObject = new NativeDog(...);
        ...
    }

    ~ManagedDog() // destructor (like Dispose() in C#)
    {
        // free unmanaged resources
        if (innerObject)
            delete innerObject;
    }

    !ManagedDog() // finalizer (like Finalize() in C#, in case
    {             // the user forgets to dispose)
        ~ManagedDog(); // call destructor
    }
}

一切都很好,我使用这样的课程:

// in C++/CLI
// this function is called from C++ code
void MyLibrary::FeedDogNative(NativeDog* nativedog)
{
    ... // (***)
}
// this function is called from C#, passes on the dog to the native function
void MyLibrary::FeedDogManaged(ManagedDog^ dog)
{
    NativeDog* rawdog = dog->innerObject;
    MyLibrary::FeedDogNative(rawdog);
}

// C# client code
void MyFunc()
{
    ManagedDog dog = new ManagedDog(...);
    MyLibrary.FeedDogManaged(dog);
}

看看有什么问题?我一开始都没有,直到很多奇怪的事情不时发生。基本上,如果在调用MyFunc()之后,当GC在本机函数FeedDogNative(上面标记为(***))中的某个位置暂停程序时,它会认为可以收集托管包装器,因为它将不再使用C#MyFunc(它是一个局部变量,不会在FeedDogManaged调用后使用),也不会在FeedDogManaged中使用。所以这实际上偶尔会发生。 GC调用Finalizer,delete是本地狗对象,即使FeedDogNative尚未使用它!所以我的非托管代码现在使用删除的指针。

我该怎样防止这种情况?我可以想到一些方法(例如假装在dog结束时假装使用FeedDogManaged),但推荐的方式是什么?

2 个答案:

答案 0 :(得分:7)

您的FeedDogManaged功能需要GC::KeepAlive()来电。这似乎是一个确切的用例。

答案 1 :(得分:4)

在托管代码中,在调用FeedDogManaged()之后添加GC.KeepAlive(dog)

http://msdn.microsoft.com/en-us/library/system.gc.keepalive(VS.71).aspx