到底是什么'固定'做的事情

本文关键字:固定 是什么 | 更新日期: 2023-09-27 18:09:12

我有一些代码看起来像这样,在一个不安全的上下文中:

 ValidatePartialOperation(array, startingOffset, runLength);
 fixed (double* _op = array)
 {
     double* op = _op + startingOffset;
     callSomething(op, runLength);
 }

我把这个copy+粘贴在几个不同的地方。但我不喜欢在多个地方有这种验证和指针运算,所以我想把这些逻辑组合成一行,看起来像这样:

 double* op = preCall(array, startingOffset, runLength);
 callSomething(op, runLength);
 postCall(array);

或者更好:

 using (double* op = preCall(array, startingOffset, runLength))
 {
     callSomething(op, runLength);
 }

但是无论发生什么,我都不能承受"固定"版本的性能损失。

我现在的计划是模仿固定语句正在做什么,但我实际上不知道那是什么。应该是有固定操作的try-catch块吧?

到底是什么'固定'做的事情

当然可以。是否能满足你的性能需求,我不知道;你应该测量一下,然后找出答案。

要在不使用fixed语句的情况下固定数组并获得指针,可以使用GCHandle对象。用"固定"句柄类型调用GCHandle.Alloc,传入数组,您将返回一个可以安全地转换为指针的IntPtr。在GCHandle上调用Free之前,数组将保持固定状态,因此确保不要丢失对GCHandle 的跟踪。只要该句柄是杰出的,您就会破坏垃圾收集器的性能。

但我的建议是使用fixed语句。这就是它的作用。

只是为了将来的读者,我想我已经很好地弄清楚了,尽管有一些有根据的猜测。

基本上,fixed似乎是c#调用c++/CLI pin_ptr的方式。这些是必须在堆栈上声明为局部变量的特殊变量。它们似乎不直接与GC通信,所以它们的重量非常轻。相反,当GC运行时,它足够聪明地搜索所有活动线程的调用堆栈,并检查是否有任何函数的变量是这些特殊的固定指针。如果是,那么它们所指向的对象将被标记为固定,并且在垃圾收集期间不会在内存中移动。

相比之下,GCHandle.Alloc(obj, GCHandleType.Pinned)实际上与GC通信,并将对象放在不移动的对象列表中。这意味着每次你执行GCHandle.AllocFree时,你都在向列表中添加和删除元素并做功。钉住指针是一个完全被动的机制,不需要做任何额外的工作。这也解释了为什么不能更改固定指针的值:只有当固定指针指向它时,它所指向的托管对象才保证是固定的。如果固定指针指向不同的对象,则现在将被固定。如果你将固定指针设置为null,即使只是一小会儿,它也不会再固定任何东西,你到目前为止所做的所有指针数学运算都将无效。

这解释了当我尝试切换到GCHandles时性能下降的原因。因此,fixed不仅是最好的工具,而且是唯一的工具,至少在性能很重要的时候是这样。即使语法有时很别扭

fixed阻止垃圾收集器在内存压缩期间重新定位您的对象。如果你不使用它,那么你的对象可以随时移动,指针将变得无效。

不,不是那样的。就像MSDN说的:固定语句阻止垃圾收集器移动内存,所以你的固定指针只要需要就保持有效。如果您正在处理一些非托管资源,这一点很重要。据我目前所知,你不能用任何using, try/catch/finally或其他任何东西来代替它。

fixed引脚变量,防止垃圾收集器在fixed块期间移动对象。

根据文档:

c#编译器只允许在固定语句中将指针赋值给托管变量。

因此,似乎您可能无法在代码中使用fixed语句。