Partilhar via


SpanOwner<T>

O SpanOwner<T> é um tipo de buffer apenas em pilha que reserva buffers de um pool de memória compartilhada. Ele essencialmente espelha a funcionalidade do MemoryOwner<T>, mas como um ref struct tipo. Isso é particularmente útil para buffers de curta vida útil que são usados apenas em código síncrono (que não exigem Memory<T> instâncias), bem como código executado em um ciclo repetitivo, pois a criação de SpanOwner<T> valores não exigirá alocações de memória.

APIs da plataforma :SpanOwner<T>, MemoryOwner<T>

Sintaxe

As mesmas características principais de MemoryOwner<T> se aplicam a este tipo também, com a exceção de ser apenas uma struct de pilha, e o facto de que não possui a implementação de IMemoryOwner<T>interface, bem como a propriedade Memory<T>. A sintaxe é praticamente idêntica à usada com MemoryOwner<T>, exceto pelas diferenças mencionadas acima.

Como exemplo, suponha que temos um método em que precisamos alocar um buffer temporário de um tamanho especificado (vamos chamar esse valor length) e, em seguida, usá-lo para executar algum trabalho. Uma primeira versão ineficiente pode ter esta aparência:

byte[] buffer = new byte[length];

// Use buffer here

Isso não é o ideal, pois estamos a alocar um novo buffer cada vez que este código é usado, e depois descartá-lo imediatamente (como mencionado nos documentos MemoryOwner<T>), o que coloca mais pressão sobre o coletor de lixo. Podemos otimizar o código acima usando 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);
}

O código acima aluga um buffer de um pool de matrizes, mas é mais detalhado e propenso a erros: precisamos ter cuidado com o try/finally bloco para garantir que sempre retornem o buffer alugado para o pool. Podemos reescrevê-lo usando o SpanOwner<T> tipo, assim:

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

A SpanOwner<T> instância alugará internamente uma matriz e cuidará de devolvê-la ao pool quando ela sair do escopo. Também não precisamos mais usar um try/finally bloco, pois o compilador C# adicionará isso automaticamente ao expandir essa using instrução. Como tal, o tipo SpanOwner<T> pode ser visto como um envoltório leve em torno das APIs ArrayPool<T>, tornando-as mais compactas e fáceis de usar, reduzindo a quantidade de código que precisa ser escrito para alugar e descartar corretamente buffers de curta duração. Você pode ver como o uso SpanOwner<T> torna o código muito mais curto e direto.

Observação

Como este é um tipo exclusivo para pilha, ele depende do padrão de tipagem de pato introduzido no C# 8. Isso é mostrado no exemplo acima: o tipo SpanOwner<T> está a ser utilizado dentro de um bloco using, apesar de o tipo não implementar a interface IDisposable e também nunca ser empacotado. A funcionalidade é exatamente a mesma: assim que o buffer sai do escopo, ele é automaticamente descartado. As APIs em SpanOwner<T> confiam nesse padrão para obter desempenho extra: assumem que o buffer subjacente nunca será descartado enquanto o tipo SpanOwner<T> estiver no escopo, e não realizam as verificações adicionais realizadas em MemoryOwner<T> para garantir que o buffer ainda esteja de fato disponível antes de retornar uma instância de Memory<T> ou Span<T> dele. Como tal, este tipo deve ser sempre usado com um using bloco ou expressão. Não fazer isso fará com que o buffer subjacente não seja retornado ao pool compartilhado. Tecnicamente, o mesmo também pode ser alcançado chamando Dispose manualmente o SpanOwner<T> tipo (que não requer C# 8), mas isso é propenso a erros e, portanto, não recomendado.

Exemplos

Pode encontrar mais exemplos nos testes unitários.