对象在执行期间被垃圾收集?

时间:2012-02-16 20:14:43

标签: .net garbage-collection c++-cli

考虑以下C ++ / CLI代码片段:

public ref class MyData
{

private:
  UnmanagedObject* pobj;

public:
  MyData()
  {
    pobj = ... // acquire unmanaged pointer
  }

  !MyData()
  {
    delete pobj;  // finalizer
    pobj = NULL;
  }

  void DoSomething()
  {
    // next line calls a native method that has no knowledge of .NET
    CallToNativeMethod(data->pobj);
  }

  static void Test()
  {
    MyData^ data = gcnew MyData();
    data->DoSomething()
  }
}

此类表示某些非托管API的.NET包装器。在我的项目中,我目前正在处理一个难以追踪的错误 - 似乎MyData的实例在方法DoSomething()仍在执行时被垃圾收集。以下是我认为发生的事情:

  1. 调用静态方法Test(),创建MyData
  2. 的实例
  3. 实例方法DoSomething()称为
  4. DoSomething()调用本机方法
  5. 当(!)正在执行本机方法时,垃圾收集器收集MyData的实例并调用终结器
  6. 终结器删除pobj,它由仍在执行的本机方法使用
  7. 嗯......程序崩溃
  8. 我的两个问题:

    1. 上述情况是否真的可行?我无法完全理解一个对象能够在实例方法仍在执行时最终确定...
    2. 解决此问题的好方法是什么(即防止对象被收集,直到“安全”这样做)?在本机方法执行之后修改任何实例变量可以解决这个问题,但它感觉很特别。

    3. 编辑(一些其他信息):代码崩溃,AccessViolationException从本机方法冒出来,可以被try..catch Test()抓住}。为了进行调试,我插入了几个日志记录调用,它们显示了以下执行顺序:

      1. 输入本机方法(但从未退出)
      2. 执行终结器
      3. 发现异常

1 个答案:

答案 0 :(得分:0)

您应该将GC::KeepAlive(data)添加到Test()方法的底部。

请注意,如果CallToNativeMethod调用另一个线程并立即返回,则托管数据对象和GC引用将很快超出范围,然后再次被垃圾回收。在这种情况下,您需要创建一种调用KeepAlive的通知代码,直到它获得信号。