到底是什么'固定'做的事情
本文关键字:固定 是什么 | 更新日期: 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.Alloc
和Free
时,你都在向列表中添加和删除元素并做功。钉住指针是一个完全被动的机制,不需要做任何额外的工作。这也解释了为什么不能更改固定指针的值:只有当固定指针指向它时,它所指向的托管对象才保证是固定的。如果固定指针指向不同的对象,则它现在将被固定。如果你将固定指针设置为null,即使只是一小会儿,它也不会再固定任何东西,你到目前为止所做的所有指针数学运算都将无效。
这解释了当我尝试切换到GCHandles时性能下降的原因。因此,fixed
不仅是最好的工具,而且是唯一的工具,至少在性能很重要的时候是这样。即使语法有时很别扭
fixed阻止垃圾收集器在内存压缩期间重新定位您的对象。如果你不使用它,那么你的对象可以随时移动,指针将变得无效。
不,不是那样的。就像MSDN说的:固定语句阻止垃圾收集器移动内存,所以你的固定指针只要需要就保持有效。如果您正在处理一些非托管资源,这一点很重要。据我目前所知,你不能用任何using
, try/catch/finally
或其他任何东西来代替它。
fixed
引脚变量,防止垃圾收集器在fixed
块期间移动对象。
根据文档:
c#编译器只允许在固定语句中将指针赋值给托管变量。
因此,似乎您可能无法在代码中使用fixed
语句。