Compartilhar via


SpanOwner<T>

O SpanOwner<T> é um tipo de buffer somente pilha que aluga buffers de um pool de memória compartilhado. Essencialmente, ele espelha a funcionalidade de MemoryOwner<T>, mas como um tipo ref struct. Isso é útil principalmente para buffers de curta duração que são usados apenas em código síncrono (que não exigem instâncias de Memory<T>), bem como o código em execução em um loop estreito, pois a criação de valores SpanOwner<T> não exigirá alocações de memória.

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

Sintaxe

Os mesmos recursos principais de MemoryOwner<T> se aplicam a esse tipo também, com exceção de ser um struct somente pilha e o fato de que ele não tem a implementação de IMemoryOwner<T>interface, bem como a propriedade Memory<T>. A sintaxe também é praticamente idêntica à usada com MemoryOwner<T>, exceto pelas diferenças mencionadas acima.

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

byte[] buffer = new byte[length];

// Use buffer here

Isso não é ideal, pois estamos alocando um novo buffer sempre que esse código é usado e, em seguida, jogando-o fora imediatamente (como mencionado nos documentos de MemoryOwner<T> também), 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 bloco try/finally para sempre retornar o buffer alugado para o pool. Podemos reescrevê-lo usando o tipo SpanOwner<T>, da seguinte maneira:

// 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 instância de SpanOwner<T> alugará internamente uma matriz e vai devolvê-la ao pool quando ela sair do escopo. Também não precisamos mais usar um bloco de try/finally, pois o compilador C# adicionará isso automaticamente ao expandir essa instrução using. Dessa forma, o tipo SpanOwner<T> pode ser visto como um wrapper leve em torno das APIs de ArrayPool<T>, o que as tornam mais compactas e fáceis de usar, reduzindo a quantidade de código que precisa ser gravado para alugar e descartar buffers de curta duração corretamente. Você pode ver que usar o SpanOwner<T> torna o código mais curto e simples.

Observação

Como este é um tipo somente de pilha, ele se baseia no padrão de IDisposable tipo duck introduzido com C# 8. Isso é mostrado no exemplo acima: o tipo SpanOwner<T> está sendo usado em um bloco using, apesar do fato de que o tipo não implementa a interface de IDisposable e também nunca está desmarcado. A funcionalidade é a mesma: assim que o buffer sai do escopo, ele é descartado automaticamente. As APIs no SpanOwner{T} dependem desse padrão para um desempenho adicional: elas pressupõem que o buffer subjacente nunca será descartado desde que o tipo SpanOwner<T> esteja no escopo e não executem as verificações adicionais feitas em MemoryOwner<T> para garantir que o buffer ainda esteja disponível antes de retornar uma instância de Memory<T> ou Span<T> dele. Dessa forma, esse tipo sempre deve ser usado com um bloco ou expressão using. 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 manualmente Dispose no tipo SpanOwner<T> (que não requer C# 8), mas isso é propenso a erros e, portanto, não é recomendado.

Exemplos

Você pode encontrar mais exemplos nos testes de unidade.