Udostępnij za pośrednictwem


SpanOwner<T>

Jest SpanOwner<T> to typ buforu tylko dla stosu, który wynajmuje z puli pamięci udostępnionej. Zasadniczo odzwierciedla funkcjonalność MemoryOwner<T>elementu , ale jako ref struct typ. Jest to szczególnie przydatne w przypadku krótkotrwałych, które są używane tylko w kodzie synchronicznym (które nie wymagają Memory<T> wystąpień), a także w pętli ścisłej, ponieważ tworzenie SpanOwner<T> wartości w ogóle nie będzie wymagać alokacji pamięci.

Interfejsy API platformy: SpanOwner<T>, MemoryOwner<T>

Składnia

Te same podstawowe funkcje MemoryOwner<T> mają zastosowanie również do tego typu, z wyjątkiem tego, że jest to tylko stos struct, i fakt, że nie ma IMemoryOwner<T> interface implementacji, a także Memory<T> właściwość. Składnia jest praktycznie identyczna z składnią używaną z MemoryOwner<T> programem , z wyjątkiem różnic wymienionych powyżej.

Załóżmy na przykład, że mamy metodę, w której musimy przydzielić tymczasowy bufor o określonym rozmiarze (nazwijmy tę wartość length), a następnie użyjemy jej do wykonania jakiejś pracy. Po pierwsze, nieefektywna wersja może wyglądać następująco:

byte[] buffer = new byte[length];

// Use buffer here

Nie jest to idealne rozwiązanie, ponieważ przydzielamy nowy bufor za każdym razem, gdy ten kod jest używany, a następnie natychmiast go wyrzuca (jak wspomniano również w MemoryOwner<T> dokumentacji), co wywiera większą presję na moduł odśmiecania pamięci. Możemy zoptymalizować powyższy kod przy użyciu polecenia 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);
}

Powyższy kod wynajmuje bufor z puli tablic, ale jest bardziej szczegółowy i podatny na błędy: musimy zachować ostrożność z try/finally blokiem, aby upewnić się, że zawsze zwraca wynajęty bufor do puli. Możemy go ponownie napisać przy użyciu SpanOwner<T> typu , w następujący sposób:

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

Wystąpienie SpanOwner<T> wewnętrznie wynajmuje tablicę i zajmie się zwróceniem jej do puli, gdy wykracza poza zakres. Nie musimy już używać try/finally bloku, ponieważ kompilator języka C# doda to automatycznie podczas rozszerzania tej using instrukcji. W związku z tym SpanOwner<T> typ może być postrzegany jako uproszczona otoka interfejsów ArrayPool<T> API, co sprawia, że są one zarówno bardziej kompaktowe, jak i łatwiejsze do użycia, zmniejszając ilość kodu, który należy zapisać w celu prawidłowego wynajmu i usuwania krótkotrwałych. Możesz zobaczyć, jak użycie SpanOwner<T> sprawia, że kod jest znacznie krótszy i prostszy.

Uwaga

Ponieważ jest to typ tylko stosu, opiera się na wzorcu typu duck wprowadzonym IDisposable w języku C# 8. Pokazano to w powyższym przykładzie: SpanOwner<T> typ jest używany w using bloku, pomimo faktu, że typ w ogóle nie implementuje interfejsu IDisposable i nigdy nie jest w polu. Funkcjonalność jest taka sama: gdy tylko bufor wykracza poza zakres, jest on automatycznie usuwany. Interfejsy API w SpanOwner{T} programie opierają się na tym wzorcu dla dodatkowej wydajności: zakładają, że bufor bazowy nigdy nie zostanie usunięty, o ile SpanOwner<T> typ jest w zakresie i nie wykonują dodatkowych kontroli, które są wykonywane w MemoryOwner<T> celu zapewnienia, że bufor jest w rzeczywistości nadal dostępny przed zwróceniem Memory<T> wystąpienia lub Span<T> z niego. W związku z tym ten typ powinien być zawsze używany z blokiem lub wyrażeniem using . Nie spowoduje to zwrócenia bazowego buforu do puli udostępnionej. Technicznie można to również osiągnąć, ręcznie wywołując Dispose typ (który nie wymaga języka C# 8), ale jest podatny na SpanOwner<T> błędy i dlatego nie jest to zalecane.

Przykłady

Więcej przykładów można znaleźć w testach jednostkowych.