【问题标题】:is it necessary to gchandle.alloc() each callback in a class?是否需要 gchandle.alloc() 类中的每个回调?
【发布时间】:2010-11-04 13:43:32
【问题描述】:

我有一个 .NT 类,它有多个代表来自本机代码的回调。是否有必要分配所有代表?我的意思是GCHandle.Alloc() 是否只保护委托或拥有委托的整个类不被收集?

【问题讨论】:

    标签: c# .net delegates callback


    【解决方案1】:

    委托有两个相关的属性,方法和目标。如果委托是为实例方法创建的,则 Target 将为非 null。只要垃圾收集器可以看到委托实例,这就会使对象保持活动状态。

    原生代码与回调问题有关。当您将委托实例传递给 pinvoked 本机函数时,P/Invoke 编组器将使用 Marshal.GetFunctionPointerForDelegate() 创建一个小存根,该存根在本机代码进行回调时生成所需的目标引用。然而,垃圾收集器看不到这个存根,因此不会找到对委托对象的引用。并收集它。本机代码的下一个回调会产生崩溃。

    为避免这种情况,您必须自己存储委托对象,以便只要本机代码可以进行回调,它就会一直被引用。将其存储在静态变量中是一个明显的解决方案。

    【讨论】:

    • 有点迟到的评论,但是否将其存储在 static 变量中也可以防止其他答案中提到的重新排列?
    • 固定一个对象是完全不同的事情,而不是这里的主题。回调代表不需要固定。
    • 其他答案(以及我检查的其他一些来源)确实提到,如果回调委托移动到不同的地址,本机层会在尝试调用它时崩溃,因为它拥有的地址是不再有效。还是我弄错了?
    【解决方案2】:

    我错了。 您不需要固定委托(另一方面,您不能固定委托,会引发异常(System.ArgumentException: Object contains non-primitive or non-blittable data.)

    参考: http://social.msdn.microsoft.com/Forums/vstudio/en-US/bd662199-d150-4fbf-a5ee-7a06af0493bb/interop-pinning-and-delegates?forum=

    Chris Brumme 的博客 http://blogs.msdn.com/cbrumme/archive/2003/05/06/51385.aspx 中的详细信息

    克里斯·布鲁姆写道: 同样,托管委托可以编组到非托管代码,在那里它们作为非托管函数指针公开。对这些指针的调用将执行非托管到托管的转换;调用约定的改变;进入正确的 AppDomain;以及任何必要的参数编组。显然,非托管函数指针必须指向一个固定地址。如果 GC 重新定位它,那将是一场灾难!这导致许多应用程序为委托创建固定句柄。这是完全没有必要的。非托管函数指针实际上是指我们动态生成以执行转换和封送处理的本机代码存根。此存根存在于 GC 堆外的固定内存中。

    但是,应用程序负责以某种方式延长委托的生命周期,直到不再有来自非托管代码的调用。本机代码存根的生命周期与委托的生命周期直接相关。一旦委托被收集,通过非托管函数指针的后续调用将崩溃或以其他方式破坏进程。在我们最近的版本中,我们添加了一个客户调试探针,它允许您干净地检测代码中的这个 - 太常见了 - 错误。如果您在开发过程中还没有开始使用 Customer Debug Probes,请查看!

    【讨论】:

      【解决方案3】:

      我认为当您仅存储委托对象时会发生错误(但通常不会)。 众所周知,托管内存将由 Garbage Collect 安排。 (这意味着托管对象的物理内存地址将被更改。)

      想象有一个长期存在的委托被本地代码调用,我们将委托设置为静态成员或类成员。但是有时候(我们不知道什么时候,我们只知道它会发生),GC 安排了内存,delegate 的物理内存可能从 0x000000A 到 0x0000010 。但是本地代码对此一无所知,对于本地代码,它只知道永远在0x000000A处调用。 所以我们不仅要存储委托对象,还要使用 GCHandle.Alloc 告诉 GC 不要移动委托对象的物理内存。那么原生代码会在回调时表现良好。

      好吧,因为 GC 不经常安排托管内存,所以对于一个短时间的委托,即使你不调用 GCHandle.Alloc ,你的代码总是“做得好”,但有时它会发疯。 现在,你知道原因了吧。

      参考: http://dotnet.dzone.com/news/net-memory-control-use-gchandl

      【讨论】:

      • 使用 GCHandle.Alloc 时不需要固定代理
      猜你喜欢
      • 1970-01-01
      • 2019-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多