C# 不安全/固定代码

Posted

技术标签:

【中文标题】C# 不安全/固定代码【英文标题】:C# Unsafe/Fixed Code 【发布时间】:2010-09-10 06:32:31 【问题描述】:

有人可以举例说明在 C# 代码中实际使用“不安全”和“固定”的好时机吗?我以前玩过它,但从未真正找到它的好用处。

考虑这段代码...

fixed (byte* pSrc = src, pDst = dst) 
    //Code that copies the bytes in a loop

相比于简单地使用...

Array.Copy(source, target, source.Length);

第二个是在.NET Framework中找到的代码,第一部分是从微软网站http://msdn.microsoft.com/en-us/library/28k1s2k6(VS.80).aspx复制的代码。

内置的 Array.Copy() 比使用不安全代码快得多。这可能只是因为第二个写得更好,第一个只是一个例子,但是你真的需要在什么情况下使用不安全/固定代码?还是这个可怜的 Web 开发人员在搞乱他头上的事情?

【问题讨论】:

【参考方案1】:

它对于与非托管代码的互操作很有用。任何传递给非托管函数的指针都需要修复(也称为固定),以防止垃圾收集器重新定位底层内存。

如果您使用的是 P/Invoke,则默认编组器将为您固定对象。有时需要执行自定义编组,有时需要固定对象的时间超过单个 P/Invoke 调用的持续时间。

【讨论】:

【参考方案2】:

我使用了不安全的块来操作位图数据。原始指针访问比 SetPixel/GetPixel 快得多。

unsafe

    BitmapData bmData = bm.LockBits(...)
    byte *bits = (byte*)pixels.ToPointer();
    // Do stuff with bits

“固定”和“不安全”通常在进行互操作或需要额外性能时使用。 IE。 String.CopyTo() 在其实现中使用不安全和固定的。

【讨论】:

我会将“显着”更改为“许多数量级”。令人难以置信的是 SetPixel 和 GetPixel 的速度有多慢。 1980 年,Apple IIe 上的像素访问速度更快。【参考方案3】:

reinterpret_cast 风格行为

如果你有点操纵,那么这将非常有用

许多高性能哈希码实现使用 UInt32 作为哈希值(这使得转换更简单)。由于 .Net 需要 Int32 用于您想要快速将 uint 转换为 int 的方法。由于实际值是什么并不重要,因此只需保留值中的所有位,重新解释转换即可。

public static unsafe int UInt32ToInt32Bits(uint x)

    return *((int*)(void*)&x);

注意命名是仿照BitConverter.DoubleToInt64Bits

继续使用散列技术,将基于堆栈的结构转换为字节* 可以轻松使用每字节散列函数:

// from the Jenkins one at a time hash function
private static unsafe void Hash(byte* data, int len, ref uint hash)

    for (int i = 0; i < len; i++)
    
        hash += data[i];
        hash += (hash << 10);
        hash ^= (hash >> 6);
    


public unsafe static void HashCombine(ref uint sofar, long data)

    byte* dataBytes = (byte*)(void*)&data;
    AddToHash(dataBytes, sizeof(long), ref sofar);

unsafe 也(从 2.0 开始)允许您使用 stackalloc。这在需要一些小的可变长度数组(如临时空间)的高性能情况下非常有用。

所有这些用途都坚定地属于“仅当您的应用程序确实需要性能时”,因此在一般用途中是不合适的,但有时您确实需要它。

fixed 当你希望与一些有用的非托管函数(有很多)进行互操作时是必需的,这些函数采用 c 样式的数组或字符串。因此,这不仅是出于性能原因,也是出于互操作场景中的正确性原因。

【讨论】:

你为什么提到 reinterpret_cast ? unsafe/fixed 的并行是什么? @Guillame07 因为直接执行一个(而不是在结构上使用联合)需要一个不安全的上下文【参考方案4】:

Unsafe 可用于(例如)使用 LockBits 快速从图像中获取像素数据。与使用托管 API 相比,性能提升了几个数量级。

【讨论】:

【参考方案5】:

当地址被传递给旧版 C DLL 时,我们必须使用固定值。由于 DLL 在函数调用之间维护了一个内部指针,如果 GC 压缩堆并移动东西,所有地狱都会崩溃。

【讨论】:

【参考方案6】:

如果您想访问 .NET 运行时之外的内容,我相信会使用不安全代码,即。它不是托管代码(没有垃圾收集等)。这包括对 Windows API 和所有爵士乐的原始调用。

【讨论】:

“不安全”并不意味着“不受管理”。 C# 中的不安全方法是允许操作指针的托管方法。但是,代码仍然是受管理的。如果您想在应用程序中使用非托管(本机)代码,可以使用 C++/CLI 及其#pragma 指令。 冒险的想法,垃圾收集器在不安全的方法/变量/对象上不活动,如果您处理现有变量并固定此类数据结构,则可以,但是如果您扩展数据,它确实会出现问题不安全的区域,如果做错了,它会吃掉内存,而不受 GBC 的控制。虽然可以有数据并使用不安全的方法对其进行处理,但如果该过程不泄漏,则没有风险,处理后 GBC 可以接管。【参考方案7】:

这告诉我 .NET 框架的设计者在解决问题空间方面做得很好——确保“托管代码”环境可以完成传统(例如 C++)方法可以处理其不安全代码的所有事情/指针。万一它不能,如果你需要它们,不安全/固定的功能就在那里。我敢肯定有人有一个需要不安全代码的例子,但在实践中似乎很少见——这才是重点,不是吗? :)

【讨论】:

用于图形操作,比 marshal.copy 更快,用于实时数据处理 ea 音频。但是您可能不会在普通的 MVC 站点中看到它,通常与 c/c++ /assembler 结合使用。

以上是关于C# 不安全/固定代码的主要内容,如果未能解决你的问题,请参考以下文章

为啥固定大小的缓冲区只能是原始类型?

unity的C#学习——不安全代码(声明不安全代码块:实现C/C++指针的创建与相关操作)

将固定大小的数组编组为 C# 类的成员不起作用

用固定值绕过 C# 中的 DLL 调用?

您只能在 C# 中的固定语句初始值设定项错误中获取未固定表达式的地址

C# Day。8 数组升级集合~~~