Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Класс 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), но этот метод подвержен ошибкам и поэтому не рекомендуется.
Примеры
Дополнительные примеры можно найти в модульных тестах.
.NET Community Toolkit