是否需要gchandle.alloc()在类中的每个回调?

时间:2010-11-04 13:43:33

标签: c# .net delegates callback

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

3 个答案:

答案 0 :(得分:8)

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

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

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

答案 1 :(得分:2)

我错了。 您不需要固定委托(另一方面,您不能固定委托,将抛出异常(System.ArgumentException:Object包含非原始或非blittable数据。)

参考: 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

中的详细信息      

Chris Brumme写道:   沿着相同的路线,托管代理可以被编组为非托管代码,在那里它们作为非托管函数指针公开。对这些指针的调用将执行非托管到托管转换;呼召惯例的变化;进入正确的AppDomain;和任何必要的论证编组。显然,非托管函数指针必须引用固定地址。如果GC重新安置那将是一场灾难!这导致许多应用程序为委托创建固定句柄。这完全没必要。非托管函数指针实际上是指我们动态生成以执行转换的本机代码存根。封送处理。此存根存在于GC堆外部的固定内存中。

     

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

答案 2 :(得分:1)

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

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

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

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