在不安全的环境中,我有一些看起来像这样的代码:
ValidatePartialOperation(array, startingOffset, runLength);
fixed (double* _op = array)
{
double* op = _op + startingOffset;
callSomething(op, runLength);
}
我有这个副本+粘贴在几个不同的地方。但我讨厌在多个地方进行这种验证和指针算法,所以我想将逻辑组合在一起,看起来像是一行:
double* op = preCall(array, startingOffset, runLength);
callSomething(op, runLength);
postCall(array);
甚至更好:
using (double* op = preCall(array, startingOffset, runLength))
{
callSomething(op, runLength);
}
但无论发生什么,我都不能失去“固定”版本的性能。
我现在的计划是模仿固定语句正在做什么,但我实际上并不知道那是什么。可能是一些带有钉扎操作的try-catch块?
答案 0 :(得分:5)
当然,你可以做到。是否能满足您的性能需求,我不知道;你应该测量并找出答案。
要修复数组并获取指针而不使用fixed
语句,可以使用GCHandle
对象。使用“固定”句柄类型调用GCHandle.Alloc
,传入数组,然后您将返回IntPtr
,您可以安全地转换为指针。在Free
调用GCHandle
之前,该数组将保持固定状态,因此确保您不会忘记GCHandle
。只要该句柄非常出色,您就会破坏垃圾收集器的性能。
但我的建议是使用fixed
语句。这就是它的用途。
答案 1 :(得分:4)
仅仅为了未来的读者,我想我已经很好地弄明白了,尽管经过一些有根据的猜测。
基本上,fixed
似乎是调用C ++ / CLI pin_ptr的C#方式。这些是必须在堆栈上声明为局部变量的特殊变量。它们似乎没有直接与GC通信,因此它们的重量非常轻。相反,当GC运行时,它足够聪明,可以搜索所有活动线程的调用堆栈,并检查是否有任何函数的变量是这些特殊的固定指针。如果它们是,它们指向的任何东西都标记为固定,并且在垃圾收集期间不会在内存中移动。
相比之下,GCHandle.Alloc(obj, GCHandleType.Pinned)
实际上与GC通信并将对象放在不移动的对象列表中。这意味着,每当您GCHandle.Alloc
然后Free
时,您就会在列表中添加和删除元素并开展工作。固定指针是一种完全被动的机制,不需要做任何额外的工作。这也解释了为什么你不能改变固定指针的值:只要固定指针指向它,它指向的管理对象只能保证固定。如果固定指针指向不同的对象,则 it 现在将被固定。如果你将固定指针设置为null,即使只是片刻,它也不再固定任何东西,你到目前为止所做的所有指针数学都会失效。
这解释了当我尝试切换到GCHandles时的性能影响。所以fixed
不仅仅是最好的工具,它只是 工具,至少在性能很重要时。即使语法有时很尴尬。
答案 2 :(得分:0)
fixed修复了在内存压缩过程中阻止垃圾收集器重定位对象的问题。如果您不使用它,那么您的对象可以随时移动,指针将变为无效。
答案 3 :(得分:0)
不,不是那样的。就像MSDN说:Fixed语句阻止垃圾收集器移动内存,这样只要需要,你的固定指针就会保持有效。如果您正在使用某些非托管资源,这一点很重要。就我目前所知,您不能将其替换为任何using
,try/catch/finally
或其他任何内容。
答案 4 :(得分:0)