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