класс 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

Эта версия не является идеальной, так как она выделяет новый буфер каждый раз при использовании этого кода, а затем немедленно отбрасывает его. Этот подход оказывает большее давление на сборщик мусора. Предыдущий код можно оптимизировать с помощью 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> можно рассматривать как легковесную оболочку для API ArrayPool<T>. Это делает их как более компактными, так и проще использовать, уменьшая объем кода, который необходимо написать для правильной аренды и удаления кратковременных буферов. Вы можете увидеть, как использование SpanOwner<T> делает код гораздо короче и проще.

Примечание.

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

Примеры

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