简体   繁体   English

究竟是什么'固定'做什么?

[英]What exactly is 'fixed' doing?

I have some code that looks like this, in an unsafe context: 在不安全的上下文中,我有一些看起来像这样的代码:

 ValidatePartialOperation(array, startingOffset, runLength);

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

And I have that copy+pasted in a couple of different places. 我将这个副本粘贴在几个不同的地方。 But I hate having that kind of validation and pointer arithmetic in multiple places, so I'd like to combine together the logic in to a single line that looks something like: 但是我讨厌在多个地方进行那种验证和指针算法,所以我想把逻辑组合成一行看起来像这样的:

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

Or even better: 甚至更好:

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

But whatever happens I can't afford to lose performance from the 'fixed' version. 但无论发生什么,我都不能失去“固定”版本的性能。

My plan right now is to mimic what the fixed statement is doing, but I don't actually know what that is. 我现在的计划是模仿固定语句正在做什么,但我实际上并不知道那是什么。 Presumably some try-catch block with a pinning operation? 可能是一些具有钉扎操作的try-catch块?

Sure, you can do that. 当然,你可以做到这一点。 Whether it will meet your performance needs, I don't know; 是否能满足您的性能需求,我不知道; you should measure and find out. 你应该测量并找出答案。

To fix an array in place and obtain a pointer without using the fixed statement you can use a GCHandle object. 要在不使用fixed语句的情况下修复数组并获取指针,可以使用GCHandle对象。 Call GCHandle.Alloc with the "pinned" handle type, passing in the array and you'll get back an IntPtr which you can safely cast to a pointer. 使用“固定”句柄类型调用GCHandle.Alloc ,传入数组,然后返回一个可以安全地转换为指针的IntPtr The array will stay pinned until Free is called on the GCHandle , so make sure you do not lose track of the GCHandle . 该阵列将保持固定直至Free为号召GCHandle ,所以一定要确保你不会失去跟踪的GCHandle You are wrecking garbage collector performance as long as that handle is outstanding. 只要该句柄非常出色,您就会破坏垃圾收集器的性能。

But my advice is to use the fixed statement. 但我的建议是使用fixed语句。 That's what it's for. 这就是它的用途。

Just for future readers, I think I've pretty well figured it out, though with a few educated guesses. 仅仅为了未来的读者,我想我已经很好地弄明白了,尽管有一些有根据的猜测。

Basically it seems that fixed is the C# way to invoke the C++/CLI pin_ptr . 基本上似乎fixed是调用C ++ / CLI pin_ptr的C#方式。 These are special variables that must be declared as local variables on the stack. 这些是必须在堆栈上声明为局部变量的特殊变量。 They don't seem to communicate with the GC directly, so they're very light weight. 它们似乎没有直接与GC通信,因此它们的重量非常轻。 Instead, when the GC runs it's smart enough to scour the callstacks of all active threads and check to see if any functions' variables are these special pinning pointers. 相反,当GC运行时,它足够聪明,可以搜索所有活动线程的调用堆栈,并检查是否有任何函数的变量是这些特殊的固定指针。 If they are, whatever they point at is marked as pinned and won't be moved in memory during garbage collection. 如果它们是,它们指向的任何内容都标记为固定,并且在垃圾收集期间不会在内存中移动。

By comparison, GCHandle.Alloc(obj, GCHandleType.Pinned) actually communicates with the GC and places the object in a list of objects not to be moved. 相比之下, GCHandle.Alloc(obj, GCHandleType.Pinned)实际上与GC通信并将对象放在不移动的对象列表中。 Which means every time you GCHandle.Alloc and then Free , you're adding and removing elements to the list and doing work. 这意味着每次你GCHandle.Alloc然后Free ,你就是在列表中添加和删除元素并开始工作。 Pinning pointers are an entirely passive mechanism and don't need to do any extra work. 固定指针是一种完全被动的机制,不需要做任何额外的工作。 That also explains why you can't change the value of a fixed pointer: the managed object it points to is only guaranteed to be fixed as long as the fixed pointer points to it. 这也解释了为什么你不能改变固定指针的值:只要固定指针指向它,它指向的管理对象只能保证固定。 And if the fixed pointer pointed to a different object, it would now be pinned instead. 如果固定指针指向不同的对象, 现在将被固定。 And if you set the fixed pointer to null, even for just a moment, it would no longer be pinning anything and all pointer math you've done so far would be invalidated. 如果你将固定指针设置为null,即使只是片刻,它将不再固定任何东西,你到目前为止所做的所有指针数学都将失效。

Which explains the performance hit when I tried to switch to GCHandles. 这解释了当我尝试切换到GCHandles时的性能损失。 So fixed isn't just the best tool, it's the only tool, at least when performance is important. 所以fixed不仅仅是最好的工具,它是唯一的工具,至少在性能很重要时。 Even if the syntax is sometimes awkward. 即使语法有时很尴尬。

fixed is preventing the garbage collector from relocating your object during memory compacting. fixed是防止垃圾收集器在内存压缩期间重定位对象。 If you don't use it, then your object can be moved at any time and pointer will became invalid. 如果您不使用它,那么您的对象可以随时移动,指针将变为无效。

No, nothing like that. 不,不是那样的。 Like MSDN says: Fixed statement prevents garbage collector to move memory, so that your fixed pointer stays valid as long as it needs to. 就像MSDN所说: 固定语句可以防止垃圾收集器移动内存,这样只要需要,你的固定指针就会保持有效。 This is important if you are working with some unmanaged resources. 如果您正在使用某些非托管资源,这一点很重要。 To my current knowledge you cannot substitute it with any using , try/catch/finally or anything else. 根据我目前的知识,你不能用任何usingtry/catch/finally或其他任何东西替换它。

fixed pins the variable, preventing the garbage collector from moving the object for the duration of the fixed block. fixed引脚变量,防止垃圾收集器在fixed块的持续时间内移动对象。

According to the documentation : 根据文件

The C# compiler only lets you assign a pointer to a managed variable in a fixed statement. C#编译器只允许您在固定语句中分配指向托管变量的指针。

Thus, it seems you may not be able to work around using the fixed statement in your code. 因此,似乎您可能无法在代码中使用fixed语句。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM