博客
关于我
内存包装类 Memory 和 Span 相关类型
阅读量:398 次
发布时间:2019-03-05

本文共 6564 字,大约阅读时间需要 21 分钟。

.NET 内存管理与缓冲区优化指南

1. 前言

本文是对微软官方文档的翻译解读。由于官方文档采用机器翻译生成,部分内容可能存在不准确或不完整的情况。如有任何疑问或发现错误,请随时指出。


2. 简介

.NET框架中的内存管理系统通过一系列强类型内存区域实现连续内存访问。主要组成包括:

  • System.Span<T>:用于访问连续内存区域,支持堆栈分配,适用于局部缓冲。
  • System.ReadOnlySpan<T>:不可变版本的Span,适用于只读场景。
  • System.Memory<T>:包装连续内存区域,支持托管堆存储,无需复制数据。
  • System.ReadOnlyMemory<T>:不可变版本的Memory,适用于只读场景。
  • System.Buffers.MemoryPool<T>:内存池管理强类型内存块,支持租用和释放。
  • System.Buffers.IMemoryOwner<T>:内存块所有权管理接口。
  • MemoryManager<T>:扩展内存管理,适用于高级场景。
  • ArraySegment<T>:数组段表示法,支持特定起始位置和长度的内存访问。
  • System.MemoryExtensions:内存扩展方法集,支持String、数组和数组段转换。

这些类型的设计目标是避免不必要的内存复制和托管堆分配,通过引用更新和偏移实现高效内存操作。


3. 内存与缓冲区使用准则

3.1 所有者、消费者与生命周期管理

在多线程和异步环境中,内存缓冲区的生命周期管理至关重要。主要包括以下核心概念:

  • 所有权:缓冲区的所有者负责其生命周期管理,包括销毁操作。
  • 消费者:允许对缓冲区进行读写操作的组件,需确保单线程访问。
  • 租约:允许组件在特定时间内成为缓冲区消费者。

以下示例展示了缓冲区的所有权、消费者和租约概念:

using System;using System.Buffers;class Program{    static void Main()    {        var buffer = CreateBuffer();        try        {            int value = Int32.Parse(Console.ReadLine());            WriteInt32ToBuffer(value, buffer);            DisplayBufferToConsole(buffer);        }        finally        {            buffer.Destroy();        }    }    private static void WriteInt32ToBuffer(int value, Buffer buffer)    {        var strValue = value.ToString();        var span = buffer.Span;        for (int ctr = 0; ctr < strValue.Length; ctr++)            span[ctr] = strValue[ctr];    }    private static void DisplayBufferToConsole(Buffer buffer)    {        Console.WriteLine($"Contents of the buffer: '{buffer}'");    }}

3.2 Memory
与所有权/消费者模型

.NET Core支持两种内存管理模型:

  • 单所有权模型:缓冲区在整个生命周期内由单一所有者管理。
  • 所有权转让模型:缓冲区的所有权可以通过 IMemoryOwner
    接口转让给其他组件。
  • 3.3 缺少所有者 的 Memory
    实例

    无需显式使用 IMemoryOwner

    即可创建 Memory
    实例,隐式所有权模型适用于单所有者场景。例如:

    using System;class Example{    static void Main()    {        Memory
    memory = new char[64]; Console.Write("Enter a number: "); var value = Int32.Parse(Console.ReadLine()); WriteInt32ToBuffer(value, memory); DisplayBufferToConsole(memory); } private static void WriteInt32ToBuffer(int value, Memory
    buffer) { var strValue = value.ToString(); strValue.AsSpan().CopyTo(buffer.Slice(0, strValue.Length).Span); } private static void DisplayBufferToConsole(Memory
    buffer) { Console.WriteLine($"Contents of the buffer: '{buffer}'"); }}

    3.4 使用准则

    在使用 Memory

    和 Span
    时,需遵守以下规则:

  • 同步 API 优先使用 Span
    :Span 提供更强的性能和功能。
  • 只读场景使用 ReadOnlySpan
    或 ReadOnlyMemory
    :确保缓冲区数据不可修改。
  • 避免 dangling references:确保方法返回后不再使用缓冲区。
  • 异步操作使用 Memory
    .Pin
    :在固定内存时确保线程安全。
  • 封装 p/invoke 方法使用 Span
    :固定内存时避免跨线程访问。
  • 包装异步 p/invoke 方法使用 Memory
    :确保内存句柄可用性。

  • 4. 示例与实践

    4.1 显式管理所有权

    以下示例展示了如何使用 IMemoryOwner

    显式管理缓冲区所有权:

    using System;using System.Buffers;class Example{    static void Main()    {        using (var owner = MemoryPool
    .Shared.Rent()) { Console.Write("Enter a number: "); try { var value = Int32.Parse(Console.ReadLine()); var memory = owner.Memory; WriteInt32ToBuffer(value, memory); DisplayBufferToConsole(memory.Slice(0, value.ToString().Length)); } catch (FormatException) { Console.WriteLine("You did not enter a valid number."); } catch (OverflowException) { Console.WriteLine($"You entered a number less than {Int32.MinValue:N0} or greater than {Int32.MaxValue:N0}."); } finally { owner.Dispose(); } } } private static void WriteInt32ToBuffer(int value, Memory
    buffer) { var strValue = value.ToString(); var span = buffer.Slice(0, strValue.Length).Span; strValue.AsSpan().CopyTo(span); } private static void DisplayBufferToConsole(Memory
    buffer) { Console.WriteLine($"Contents of the buffer: '{buffer}'"); }}

    4.2 隐式管理所有权

    以下示例展示了如何使用隐式所有权:

    using System;class Example{    static void Main()    {        Memory
    memory = new char[64]; Console.Write("Enter a number: "); var value = Int32.Parse(Console.ReadLine()); WriteInt32ToBuffer(value, memory); DisplayBufferToConsole(memory); } private static void WriteInt32ToBuffer(int value, Memory
    buffer) { var strValue = value.ToString(); strValue.AsSpan().CopyTo(buffer.Slice(0, strValue.Length).Span); } private static void DisplayBufferToConsole(Memory
    buffer) { Console.WriteLine($"Contents of the buffer: '{buffer}'"); }}

    4.3 异步操作示例

    以下示例展示了如何在异步操作中使用 Memory

    using System;using System.Buffers;using System.Runtime.InteropServices;class Example{    static async Task
    ManagedWrapperAsync(Memory
    data) { var tcs = new TaskCompletionSource
    (); var state = new MyCompletedCallbackState { Tcs = tcs }; var pState = (IntPtr)GCHandle.Alloc(state); var memoryHandle = data.Pin(); state.MemoryHandle = memoryHandle; try { int result = ExportedAsyncMethod((byte*)memoryHandle.Pointer, data.Length, pState, _callbackPtr); if (result != PENDING) { MyCompletedCallbackImplementation(pState, result); } return result; } catch { ((GCHandle)pState).Free(); memoryHandle.Dispose(); throw; } return tcs.Task; } private static void MyCompletedCallbackImplementation(IntPtr state, int result) { GCHandle handle = (GCHandle)state; var actualState = (MyCompletedCallbackState)handle.Target; handle.Free(); actualState.MemoryHandle.Dispose(); if (error) { actualState.Tcs.SetException(...); } else { actualState.Tcs.SetResult(result); } } private static IntPtr GetCompletionCallbackPointer() { OnCompletedCallback callback = MyCompletedCallbackImplementation; GCHandle.Alloc(callback); return Marshal.GetFunctionPointerForDelegate(callback); } private class MyCompletedCallbackState { public TaskCompletionSource
    Tcs; public MemoryHandle MemoryHandle; }}

    5. 最佳实践

  • 优先使用 Span
    :Span 提供更高效的内存操作,适用于局部缓冲。
  • 只读场景使用 ReadOnlySpan
    :确保缓冲区数据不被修改。
  • 显式管理所有权:使用 IMemoryOwner
    确保缓冲区的生命周期。
  • 避免 dangling references:确保方法返回后缓冲区不再被使用。
  • 异步操作使用 Memory
    .Pin
    :确保内存句柄生命周期正确。
  • 封装异步方法使用 Memory
    :确保固定内存的线程安全。
  • 通过遵循这些最佳实践,可以在内存管理和缓冲区使用中实现高效、安全且可靠的代码。

    转载地址:http://bdozz.baihongyu.com/

    你可能感兴趣的文章
    Objective-C实现对称矩阵压缩存储(附完整源码)
    查看>>
    Objective-C实现寻找欧拉路径/回路(附完整源码)
    查看>>
    Objective-C实现导弹跟踪算法(附完整源码)
    查看>>
    Objective-C实现将 base64 字符串转换为字节数组算法(附完整源码)
    查看>>
    Objective-C实现将位转换为浮点数bitsToFloat算法(附完整源码)
    查看>>
    Objective-C实现将列表向右旋转 k 个位置算法(附完整源码)
    查看>>
    Objective-C实现将字符串中大写字母转换为小写字母(附完整源码)
    查看>>
    Objective-C实现将字符串从一个基转换为另一个基算法(附完整源码)
    查看>>
    Objective-C实现将字节数组转换为 base64 编码算法(附完整源码)
    查看>>
    Objective-C实现将彩色图像转换为负片算法(附完整源码)
    查看>>
    Objective-C实现将无符号整数n变成成d进制表示的字符串s(附完整源码)
    查看>>
    Objective-C实现将给定的 utf-8 字符串编码为 base-16算法(附完整源码)
    查看>>
    Objective-C实现将给定的字符串编码为 base32算法(附完整源码)
    查看>>
    Objective-C实现小根堆(附完整源码)
    查看>>
    Objective-C实现局域网双向通信(附完整源码)
    查看>>
    Objective-C实现局部最大值点数算法(附完整源码)
    查看>>
    Objective-C实现屏幕捕获功能( 附完整源码)
    查看>>
    Objective-C实现峰值信噪比算法(附完整源码)
    查看>>
    Objective-C实现已线段的形式求曲线长算法(附完整源码)
    查看>>
    Objective-C实现已递归的方式找到一个数字数组的最大值算法(附完整源码)
    查看>>