如何固定一个字节数组?

24

我想要固定一个10MB长的字节数组,以便托管和非托管代码都可以使用它。

我的场景是我有一个非托管的驱动程序,它从设备中读取一些数据并将其写入到大数组中,而托管应用程序只是读取那些数据。

类似这样:

byte[] dataArray = new byte[10*1024*1024];

我希望固定dataArray,以防止垃圾回收将其移动。

当我运行应用程序时,实际上会发生什么情况?我会得到一个DataAbortApplication错误。在互联网上阅读后,我发现我应该固定dataArray以避免此错误。如何/应该怎么做?


1
请查看 fixed 语句 http://msdn.microsoft.com/zh-cn/library/f58wzh21.aspx - Tawnos
可能是 https://dev59.com/6mrXa4cB1Zd3GeqPA52P 的重复问题。 - LB2
@FabianBigler:你的意思是我不能在托管代码和非托管代码之间共享大数组吗? - ShrShr
6
这个数组无法移动,因为它太大了,所以被分配到了大对象堆上。无论你的问题是什么,肯定与其他东西有关。你的“DataAbortApplication”是一个没有意义的诊断。 - Hans Passant
2个回答

35

有两种方法可以实现这个。第一种是使用 fixed 语句:

unsafe void UsingFixed()
{
    var dataArray = new byte[10*1024*1024];
    fixed (byte* array = dataArray)
    {
        // array is pinned until the end of the 'fixed' block
    }
}

不过,您似乎希望数组在较长时间内被固定。您可以使用 GCHandle 来实现这一点:

void UsingGCHandles()
{
    var dataArray = new byte[10*1024*1024];
    var handle = GCHandle.Alloc(dataArray, GCHandleType.Pinned);

    // retrieve a raw pointer to pass to the native code:
    IntPtr ptr = handle.AddrOfPinnedObject();

    // later, possibly in some other method:
    handle.Free();
}

6
如果托管代码和非托管代码在同一进程中,则此方法可行。若需要进行跨进程共享,则需使用内存映射文件。 - Chris Hinton
1
看起来自2014年以来GCHandle API已经发生了一些变化。现在,你需要这样做:var ptr = GCHandle.ToIntPtr(handle)ToIntPtr 看起来已经被静态化了。 - oscilatingcretin
2
使用handle.AddrOfPinnedObject()代替handle.ToIntPtr() - hillin
@hillin 我冒昧地将您的修复应用到了这个回答中。如果托管环境需要暂时完全忘记数组和句柄,并将 IntPtr 存储在不受管理的内存中作为不透明句柄,则可以使用 ToIntPtr 和 FromIntPtr。最好使用 GCHandleType.Normal 进行操作,因为长时间固定会干扰 GC。 - relatively_random

4
这里有一个类,可以用来锁定一个字节数组,直到被处理完毕后才释放。但是在你的情况下,似乎更适合使用内存映射文件。
public class PinnedBuffer : IDisposable
{
    public GCHandle Handle { get; }
    public byte[] Data { get; private set; }

    public IntPtr Ptr
    {
        get
        {
            return Handle.AddrOfPinnedObject();
        }
    } 

    public PinnedBuffer(byte[] bytes)
    {
        Data = bytes;
        Handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            Handle.Free();
            Data = null;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接