Compartir a través de


SpanOwner<T>

El SpanOwner<T> es un tipo de búfer de solo pila que alquila búferes de un grupo de memoria compartido. Básicamente refleja la funcionalidad de MemoryOwner<T>, pero como un tipo ref struct. Esto es especialmente útil para los búferes de corta duración que solo se usan en código sincrónico (que no requieren instancias de Memory<T> ), así como código que se ejecuta en un bucle ajustado, ya que la creación de valores SpanOwner<T> no requerirá asignaciones de memoria en absoluto.

API de la plataforma: SpanOwner<T>, MemoryOwner<T>

Sintaxis

Las mismas características principales de MemoryOwner<T>también se aplican a este tipo, a excepción de que sea structde solo pila, y el hecho de que carece de la implementación IMemoryOwner<T> interface, así como la propiedad Memory<T>. La sintaxis es prácticamente idéntica a la que se usa con MemoryOwner<T>, excepto las diferencias mencionadas anteriormente.

Por ejemplo, supongamos que tenemos un método en el que necesitamos asignar un búfer temporal de un tamaño especificado (vamos a llamar a este valor length) y, a continuación, usarlo para realizar algún trabajo. Una primera versión ineficaz podría tener este aspecto:

byte[] buffer = new byte[length];

// Use buffer here

Esto no es ideal, ya que asignamos un nuevo búfer cada vez que se usa este código y, a continuación, lo elimina inmediatamente (como se menciona en los documentos MemoryOwner<T> ), lo que pone más presión en el recolector de elementos no utilizados. Podemos optimizar el código anterior mediante 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);
}

El código anterior alquila un búfer de un grupo de matrices, pero es más detallado y propenso a errores: es necesario tener cuidado con el bloque try/finally para asegurarse de devolver siempre el búfer alquilado al grupo. Podemos volver a escribirlo mediante el tipo SpanOwner<T>, de la siguiente manera:

// 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

La instancia SpanOwner<T> alquilará internamente una matriz y se encargará de devolverla al grupo cuando salga del ámbito. Ya no es necesario usar un bloque try/finally, ya que el compilador de C# lo agregará automáticamente al expandir esa instrucción using. Por lo tanto, el tipo SpanOwner<T> se puede ver como un contenedor ligero alrededor de las API ArrayPool<T>, lo que hace que sean más compactos y fáciles de usar, lo que reduce la cantidad de código que se debe escribir para alquilar y eliminar búferes de corta duración correctamente. Puede ver cómo el uso SpanOwner<T> hace que el código sea mucho más corto y sencillo.

Nota:

Como se trata de un tipo de solo pila, se basa en el patrón IDisposable tipo duck introducido con C# 8. Esto se muestra en el ejemplo anterior: el tipo SpanOwner<T> se usa dentro de un bloque using a pesar del hecho de que el tipo no implementa la interfaz IDisposable en absoluto y tampoco está boxeado. La funcionalidad es igual: en cuanto el búfer sale del ámbito, se elimina automáticamente. Las API SpanOwner{T} dependen de este patrón para obtener un rendimiento adicional: suponen que el búfer subyacente nunca se eliminará siempre que el tipo SpanOwner<T> esté en el ámbito y no realice las comprobaciones adicionales que se realizan en MemoryOwner<T> para asegurarse de que el búfer todavía está disponible antes de devolver una instancia de Memory<T> o Span<T> de ella. Por lo tanto, este tipo siempre debe usarse con un bloque o expresión using. Si no lo hace, el búfer subyacente no se devolverá al grupo compartido. Técnicamente, también se puede lograr mediante una llamada manual Dispose en el tipoSpanOwner<T> (que no requiere C# 8), pero es propenso a errores y, por tanto, no se recomienda.

Ejemplos

Encontrará más ejemplos en las pruebas unitarias.