Поделиться через


SpanOwner<T>

Это SpanOwner<T> тип буфера только для стека, который арендует буферы из общего пула памяти. Он по сути отражает функциональные возможности MemoryOwner<T>, но как ref struct тип. Это особенно полезно для кратковременных буферов, которые используются только в синхронном коде (которые не требуют Memory<T> экземпляров), а также для кода, выполняющегося в жестком цикле, так как создание SpanOwner<T> значений не требует выделения памяти вообще.

API платформы: SpanOwner<T>, MemoryOwner<T>

Синтаксис

Те же основные функции MemoryOwner<T> , которые применяются к этому типу, кроме того, за исключением того, что он является только стеком struct, и тот факт, что он не имеет IMemoryOwner<T> interface реализации, а также Memory<T> свойство. Синтаксис практически идентичен тому, который также используется, MemoryOwner<T> за исключением указанных выше различий.

Например, предположим, что у нас есть метод, в котором необходимо выделить временный буфер указанного размера (давайте вызовем это значение length), а затем использовать его для выполнения некоторых действий. Первая, неэффективная версия может выглядеть следующим образом:

byte[] buffer = new byte[length];

// Use buffer here

Это не идеально, так как мы выделяем новый буфер каждый раз, когда этот код используется, а затем бросает его сразу (как упоминалось в MemoryOwner<T> документации, а также), что приводит к большему давлению на сборщик мусора. Мы можем оптимизировать приведенный выше код с помощью ArrayPool<T>:

// Using directive to access the ArrayPool<T> type
using System.Buffers;

int[] buffer = ArrayPool<int>.Shared.Rent(length);

try
{
    // Slice the span, as it might be larger than the requested size
    Span<int> span = buffer.AsSpan(0, length);

    // Use the span here
}
finally
{
    ArrayPool<int>.Shared.Return(buffer);
}

Приведенный выше код арендует буфер из пула массивов, но это более подробный и подверженный ошибкам: мы должны быть осторожны с try/finally блоком, чтобы всегда возвращать арендованный буфер в пул. Его можно переписать с помощью SpanOwner<T> типа, например:

// Be sure to include this using at the top of the file:
using Microsoft.Toolkit.HighPerformance.Buffers;

using SpanOwner<int> buffer = SpanOwner<int>.Allocate(length);

Span<int> span = buffer.Span;

// Use the span here, no slicing necessary

Экземпляр SpanOwner<T> будет внутренне арендуть массив и будет заботиться о возвращении его в пул, когда он выходит из области. Мы больше не должны использовать try/finally блок, так как компилятор C# добавит это автоматически при расширении этой using инструкции. Таким образом, SpanOwner<T> тип можно рассматривать как упрощенную оболочку вокруг ArrayPool<T> API, что делает их как более компактными, так и проще использовать, уменьшая объем кода, который необходимо записать для правильной аренды и удаления коротких буферов. Вы можете увидеть, как использовать SpanOwner<T> код гораздо короче и проще.

Примечание.

Так как это тип только для стека, он зависит от шаблона типа уток, введенного IDisposable в C# 8. Это показано в приведенном выше примере: SpanOwner<T> тип используется в using блоке, несмотря на то, что тип не реализует IDisposable интерфейс вообще, и он также никогда не задается. Функциональные возможности совпадают: как только буфер выходит из области, он автоматически удаляется. API в SpanOwner{T} зависимости от этого шаблона для дополнительной производительности: предполагается, что базовый буфер никогда не будет удален до тех пор, пока SpanOwner<T> тип находится в области, и они не выполняют дополнительные проверки, которые MemoryOwner<T> выполняются, чтобы убедиться, что буфер по-прежнему доступен перед возвратом Memory<T> или Span<T> экземпляром из него. Таким образом, этот тип всегда должен использоваться с блоком или выражением using . Это приведет к тому, что базовый буфер не будет возвращен в общий пул. Технически же можно также достичь путем ручного SpanOwner<T> вызова Dispose типа (который не требует C# 8), но это подвержено ошибкам и поэтому не рекомендуется.

Примеры

Дополнительные примеры можно найти в модульных тестах.