Condividi tramite


SpanOwner<T>

SpanOwner<T> è un tipo di buffer solo stack che noleggia i buffer da un pool di memoria condivisa. Rispecchia essenzialmente la funzionalità di MemoryOwner<T>, ma come ref struct tipo . Ciò è particolarmente utile per i buffer di breve durata usati solo nel codice sincrono (che non richiedono Memory<T> istanze), nonché per il codice in esecuzione in un ciclo stretto, perché la creazione SpanOwner<T> di valori non richiederà affatto allocazioni di memoria.

API della piattaforma: SpanOwner<T>, MemoryOwner<T>

Sintassi

Le stesse funzionalità di base di MemoryOwner<T> si applicano anche a questo tipo, ad eccezione del fatto che si tratta di un solo stack structe del fatto che manca l'implementazione IMemoryOwner<T> interface , nonché la Memory<T> proprietà . La sintassi è praticamente identica a quella usata anche con MemoryOwner<T> , ad eccezione delle differenze indicate in precedenza.

Si supponga, ad esempio, di avere un metodo in cui è necessario allocare un buffer temporaneo di una dimensione specificata (chiamiamo questo valore length) e quindi usarlo per eseguire alcune operazioni. Una prima versione inefficiente potrebbe essere simile alla seguente:

byte[] buffer = new byte[length];

// Use buffer here

Questo non è l'ideale, poiché allocare un nuovo buffer ogni volta che viene usato questo codice e quindi eliminarlo immediatamente (come indicato anche nella MemoryOwner<T> documentazione), che mette più pressione sul Garbage Collector. È possibile ottimizzare il codice precedente 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);
}

Il codice precedente noleggia un buffer da un pool di matrici, ma è più dettagliato e soggetto a errori: è necessario prestare attenzione al try/finally blocco per assicurarsi di restituire sempre il buffer affittato al pool. È possibile riscriverlo usando il SpanOwner<T> tipo , come illustrato di seguito:

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

L'istanza SpanOwner<T> noleggierà internamente una matrice e si occuperà di restituirla al pool quando esce dall'ambito. Non è più necessario usare un try/finally blocco, perché il compilatore C# lo aggiungerà automaticamente durante l'espansione di tale using istruzione. Di conseguenza, il SpanOwner<T> tipo può essere visto come un wrapper leggero intorno alle ArrayPool<T> API, che li rende sia più compatta che più facile da usare, riducendo la quantità di codice che deve essere scritta correttamente per noleggiare ed eliminare buffer di breve durata. È possibile vedere come l'uso SpanOwner<T> di rende il codice molto più breve e più semplice.

Nota

Poiché si tratta di un tipo solo stack, si basa sul modello tipizzato IDisposable anatra introdotto con C# 8. Come illustrato nell'esempio precedente: il SpanOwner<T> tipo viene usato all'interno di un using blocco nonostante il fatto che il tipo non implementa affatto l'interfaccia IDisposable e non viene mai sottoposto a boxing. La funzionalità è identica: non appena il buffer esce dall'ambito, viene eliminato automaticamente. Le API in SpanOwner{T} si basano su questo modello per ottenere prestazioni aggiuntive: presuppongono che il buffer sottostante non venga mai eliminato finché il SpanOwner<T> tipo è nell'ambito e non eseguono i controlli aggiuntivi eseguiti per MemoryOwner<T> assicurarsi che il buffer sia effettivamente disponibile prima di restituire un'istanza Memory<T> o Span<T> da essa. Di conseguenza, questo tipo deve essere sempre usato con un blocco o un'espressione using . In caso contrario, il buffer sottostante non verrà restituito al pool condiviso. Tecnicamente lo stesso può essere ottenuto anche chiamando Dispose manualmente sul SpanOwner<T> tipo (che non richiede C# 8), ma che è soggetto a errori e quindi non consigliato.

Esempi

Altri esempi sono disponibili negli unit test.