2011-03-28 72 views
7

我从C#应用程序中调用CopyFileEx,并将匿名委托传递给LPPROGRESS_ROUTINE参数以获取有关文件复制进度的通知。我需要固定匿名代理吗?

我的问题是,匿名委托是否需要固定,为什么(或为什么不)。

另外,确实如果答案变化:

  1. CopyFileEx不堵。
  2. 如果我传入了一个不是匿名的委托。

谢谢!

回答

3

您不需要固定它,但只要复制正在进行中,您确实需要保留对其的引用。

由非托管代码调用的thunk是固定的,但您必须确保代理不是垃圾回收 - 因此是参考。

+0

如果匿名方法是一个局部变量,它认为“活着”只要CopyFileEx还没有回来? – SpeksETC 2011-03-28 21:12:09

+0

相当肯定它是 – sehe 2011-03-28 21:17:18

+0

@sehe,@SpeksETC:很确定,是吗?在说明书中究竟是这样说的?我的规范副本说相反,即“*编译器可能会生成代码,导致变量的存储寿命比其包含的块*更短。” – 2011-03-28 21:28:45

6

代表不需要是固定。一个被管理对象是固定的如果不能被移动由垃圾收集器。如果编组信息是正确的,那么编组层将确保传递一个指向不动的指针。

但是,上面的注释提示局部变量可能会使委托人保持活动状态表示对变量生存期的误解。我提到你的规范,其中指出:

本地变量的实际生命周期是实现相关的。例如,编译器可以静态地确定块中的局部变量仅用于该块的一小部分。使用这种分析,编译器可以生成代码,导致变量的存储比其​​包含的块的生命周期更短。 由本地参考变量中所指的存储独立地回收该本地参考变量

换言之的寿命,如果你说:

void M() 
{ 
    Foo foo = GetAFoo(); 
    UnmanagedLibrary.DoSomethingToFoo(foo); 
} 

然后抖动被允许说“你知道,我看到没有托管代码曾经在非托管调用被调用后再次使用foo;因此我可以积极地从当时的另一个线程中收回该对象的存储。这意味着当非托管调用突然在另一个线程上解除分配时,它可以在该对象上工作。

如果Foo具有析构函数,这特别讨厌。当对象被非托管库使用时,终止代码可能会在另一个线程上运行,而天堂只知道会导致什么样的灾难。

在这种情况下,您需要使用KeepAlive保持托管对象的活动状态。不要依赖局部变量;局部变量具体记录为而不是保证保持活力。

详情请参阅http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx

+0

谢谢埃里克。我有点困惑,因为Chris Brumme声明:“PInvoke会将数据复制到GC堆外的固定内存中,或者将内存固定在GC堆中,并将这些字节直接暴露给非托管代码,无论哪种情况, t需要明确的pin - 只要在PInvoke调用期间访问这些字节的范围。“在http://blogs.msdn.com/b/cbrumme/archive/2003/05/06/51385 .aspx?PageIndex = 1#注释 - 这是否与代表有关?我认为,如果pinvoke层引脚数据,GC不会收集... – SpeksETC 2011-03-28 21:51:44

+2

@SpeksETC:你链接到的文章明确指出你的假设是错误的。它说“**但是,应用程序负责以某种方式延长委托的生命周期,直到不再有来自非托管代码**的调用发生。“ – 2011-03-28 21:59:52

+0

@Eric Lippert:难道那个陈述只是指异步非托管调用吗?似乎否定了他在评论中写道:”如果非托管的被调用者只需要访问缓冲区以便进行调用,那么PInvoke封送层一般会固定这段持续时间“除非编组层对待不同的代表... – SpeksETC 2011-03-28 22:26:58

2

从以下msdn好像都寄托和GC.KeepAlive是不需要在这种情况下,因为CopyFileEx是同步的。具体而言,它说:

“通常,您不必担心代理的生命周期。无论何时将代理传递给非托管代码,CLR都会确保代理在调用过程中处于活动状态。本机代码会将指针副本保留在调用范围之外,并打算稍后通过该指针进行回调,您可能需要使用GCHandle来显式阻止垃圾收集器收集代理。“

因为CopyFileEx没有指针不断超越呼叫的跨度的功能,我们不应该需要调用保持活动。

+0

即使在这种情况下遇到问题的机会“非常渺茫”,这也是不正确的。正如其他答案中指出的那样,GC在另一个线程上* *可能会在调用开始后的任意时刻回收作为参数提供的委托。对于一个短暂的同步调用,这是一个“通常情况下,你不必担心”,但唯一能保证的方法就是确保GC *不会通过使用KeepAlive来回收它或以其他方式保持强烈的参考。 – user2864740 2015-02-16 23:10:36

+0

换句话说:如果同步呼叫跨越几分钟,您是否会感觉舒适*不*使用KeepAlive?我肯定不会被这种“通常”不被“永远”咬伤的人咬住;甚至几秒钟时间才能完成一个CPU,并且有足够的空闲时间让GC太饿。确保完全按照要求控制使用期限。 – user2864740 2015-02-16 23:12:38