3

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

 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 块?

4

5 回答 5

5

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

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

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

于 2013-04-15T20:14:07.303 回答
4

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

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

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

这解释了当我尝试切换到 GCHandles 时性能受到的影响。所以fixed它不仅是最好的工具,而且是唯一的工具,至少在性能很重要的时候是这样。即使语法有时很尴尬。

于 2013-04-16T17:34:50.443 回答
0

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

于 2013-04-15T19:00:45.833 回答
0

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

于 2013-04-15T19:01:50.057 回答
0

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

根据文档

C# 编译器仅允许您在固定语句中分配指向托管变量的指针。

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

于 2013-04-15T19:01:58.963 回答