【问题标题】:C# access unmanaged array using Memory<T> or ArraySegment<T>?C# 使用 Memory<T> 或 ArraySegment<T> 访问非托管数组?
【发布时间】:2018-09-05 17:18:43
【问题描述】:

随着在 C# 7.2 中引入 MemorySpanArraySegment,我想知道是否可以将非托管数组表示为可枚举的对象,它存在于堆上。

后一个要求排除了Span,它基本上实现了我想要的:例如

unsafe { bytes = new Span<byte>((byte*)ptr + (index * Width), Width); 

是否可以对ArraySegmentMemory 做同样的事情?他们的构造函数只接受byte[],也许有办法欺骗C# 传递byte* 而不是byte[]

【问题讨论】:

  • 为什么要使用SpanMemory 执行此操作?您可以Marshal.GlobalHAlloc 并获得IntPtr 直接使用一组非托管内存。您可能能够以某种方式将其转换为可以传递给这些对象的byte[]。您是要创建一个新的内存块来处理,还是要访问另一个进程内存块?
  • 我正在使用 SkiaSharp 加载我逐字节迭代的图像。目前我使用提供的属性将数据复制为 byte[] 以使用,但 Skia 还提供了指向非托管内存的本机指针,我想探索它,因为它为我节省了内存副本。
  • @RonBeyer Memory&lt;byte&gt; 实际上是完美的...
  • @MarcGravell 我同意现在我了解用例是什么,但我不知道自定义 MemoryManager&lt;T&gt;,谢谢!

标签: c# unmanaged-memory


【解决方案1】:

Memory&lt;T&gt; 可以,但您需要创建自己的 MemoryManager&lt;T&gt;。别担心 - 这并不像听起来那么可怕 - here's one I wrote earlier...:

/// <summary>
/// A MemoryManager over a raw pointer
/// </summary>
/// <remarks>The pointer is assumed to be fully unmanaged, or externally pinned - no attempt will be made to pin this data</remarks>
public sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T>
    where T : unmanaged
{
    private readonly T* _pointer;
    private readonly int _length;

    /// <summary>
    /// Create a new UnmanagedMemoryManager instance at the given pointer and size
    /// </summary>
    /// <remarks>It is assumed that the span provided is already unmanaged or externally pinned</remarks>
    public UnmanagedMemoryManager(Span<T> span)
    {
        fixed (T* ptr = &MemoryMarshal.GetReference(span))
        {
            _pointer = ptr;
            _length = span.Length;
        }
    }
    /// <summary>
    /// Create a new UnmanagedMemoryManager instance at the given pointer and size
    /// </summary>
    public UnmanagedMemoryManager(T* pointer, int length)
    {
        if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
        _pointer = pointer;
        _length = length;
    }
    /// <summary>
    /// Obtains a span that represents the region
    /// </summary>
    public override Span<T> GetSpan() => new Span<T>(_pointer, _length);

    /// <summary>
    /// Provides access to a pointer that represents the data (note: no actual pin occurs)
    /// </summary>
    public override MemoryHandle Pin(int elementIndex = 0)
    {
        if (elementIndex < 0 || elementIndex >= _length)
            throw new ArgumentOutOfRangeException(nameof(elementIndex));
        return new MemoryHandle(_pointer + elementIndex);
    }
    /// <summary>
    /// Has no effect
    /// </summary>
    public override void Unpin() { }

    /// <summary>
    /// Releases all resources associated with this object
    /// </summary>
    protected override void Dispose(bool disposing) { }
}

现在你可以使用了:

var mgr = new UnmanagedMemoryManager((byte*)ptr + (index * Width), Width);
Memory<byte> memory = mgr.Memory;

memory 可以存储在堆上。

但是,为了尽量减少分配,您可能希望创建一个单个 UnmanagedMemoryManager&lt;byte&gt; 覆盖整个 区域 - 仅一次 - 然后在 @ 上使用 .Slice(...) 987654329@ 代表整个区域。这样你就有了一个对象和很多切片(切片是结构,而不是对象)。

请注意,此实现假定您将在其他地方控制内存的生命周期 - 这里的 Dispose() 不会尝试通过 Marshal 等释放内存。

【讨论】:

  • UnmanagedMemoryManager - 如果我见过一个悖论,这是一个合法的使用。
  • @AaronLS 这似乎有点矛盾,我同意
  • @AaronLS 也许它需要一个经理因为它是“不受管理的”? ;-)
  • @MarcGravell 这太棒了。我需要做些什么特别的事情才能使用它吗?我得到The type or namespace name 'unmanaged' could not be found。已导入 System.BuffersInteropServices
  • 我删除了unmanaged 接口和所有泛型,它现在可以工作了。
猜你喜欢
  • 1970-01-01
  • 2021-07-31
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 2021-05-16
  • 1970-01-01
  • 2015-12-04
  • 1970-01-01
相关资源
最近更新 更多