防止委托被垃圾回收 [英] Prevent delegate from being garbage collected

查看:173
本文介绍了防止委托被垃圾回收的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我当前正在尝试将C#委托编组为C ++函数指针,并且查看了来自Microsoft的示例

  // MarshalDelegate1.cpp 
//编译为:/ clr
#include< iostream>

使用名称空间System;
使用名称空间System :: Runtime :: InteropServices;

#pragma非托管

//声明一个带有两个int参数的非托管函数类型
//注意使用__stdcall与托管代码$ b $兼容b typedef int(__stdcall * ANSWERCB)(int,int);复制代码

int TakesCallback(ANSWERCB fp,int n,int m){
printf_s( [非托管]获得了回调地址,将其称为... \n);
return fp(n,m);
}

#pragma管理的

公共委托int GetTheAnswerDelegate(int,int);

int GetNumber(int n,int m){
Console :: WriteLine( [managed] callback!);
返回n + m;
}

int main(){
GetTheAnswerDelegate ^ fp = gcnew GetTheAnswerDelegate(GetNumber);
GCHandle gch = GCHandle :: Alloc(fp);
IntPtr ip = Marshal :: GetFunctionPointerForDelegate(fp);
ANSWERCB cb = static_cast< ANSWERCB>(ip.ToPointer());
Console :: WriteLine( [托管]发送委托作为回调...);

//强制垃圾收集周期证明
//委托人没有被处置
GC :: Collect();

int答案= TakesCallback(cb,243,257);

//发布对委托
的引用gch.Free();
}

GCHandle :: Alloc()的调用应该防止垃圾收集器收集委托。但是我的理解是,变量 GetTheAnswerDelegate ^ fp 已经使该代理保持活动状态,因为它是一个根对象,即使我删除对GCHandle的调用,它也足够确定,该示例仍然有效。仅当我内联这样的委托实例化时:

  IntPtr ip = Marshal :: GetFunctionPointerForDelegate(gcnew GetTheAnswerDelegate(GetNumber)); 

然后我看到了崩溃。



那么微软的示例是错误的还是我错过了什么?

解决方案

您缺少使用调试器对局部变量的生存期的影响。附加调试器后,抖动会标记使用中的变量,直到方法结束。使调试可靠很重要。但是,这也会阻止GC.Collect()调用收集委托对象。



在没有调试器的情况下运行程序的Release版本时,此代码将崩溃。 / p>

有关调试构建行为对垃圾收集器影响的深入解答,请参见此帖子


I'm currently trying to marshal a C# delegate to a C++ function pointer and I looked at the example from Microsoft:

// MarshalDelegate1.cpp
// compile with: /clr
#include <iostream>

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma unmanaged

// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);

int TakesCallback(ANSWERCB fp, int n, int m) {
   printf_s("[unmanaged] got callback address, calling it...\n");
   return fp(n, m);
}

#pragma managed

public delegate int GetTheAnswerDelegate(int, int);

int GetNumber(int n, int m) {
   Console::WriteLine("[managed] callback!");
   return n + m;
}

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
   GCHandle gch = GCHandle::Alloc(fp);
   IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
   ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
   Console::WriteLine("[managed] sending delegate as callback...");

// force garbage collection cycle to prove
// that the delegate doesn't get disposed
   GC::Collect();

   int answer = TakesCallback(cb, 243, 257);

// release reference to delegate
   gch.Free();
}

The call to GCHandle::Alloc() is supposed to prevent the garbage collector from collecting the delegate. But my understanding is that the variable GetTheAnswerDelegate^ fp already keeps the delegate alive, because it is a root object and sure enough even when I remove the calls to GCHandle the example still works. Only when I inline the delegate instantiation like this:

IntPtr ip = Marshal::GetFunctionPointerForDelegate(gcnew GetTheAnswerDelegate(GetNumber));

then I'm seeing a crash.

So is the example from Microsoft wrong or did I miss something?

解决方案

You are missing the effect that using a debugger has on the lifetime of local variables. With a debugger attached, the jitter marks the variables in use until the end of the method. Important to make debugging reliable. This however also prevents the GC.Collect() call from collecting the delegate object.

This code will crash when you run the Release build of your program without a debugger.

An in-depth answer on the affect of Debug build behavior on the garbage collector is available in this post

这篇关于防止委托被垃圾回收的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆