Sdílet prostřednictvím


SpanOwner<T>

Jedná se SpanOwner<T> o typ vyrovnávací paměti jen pro zásobník, který pronajímá vyrovnávací paměti ze sdíleného fondu paměti. V podstatě zrcadlí funkčnost MemoryOwner<T>, ale jako ref struct typ. To je zvlášť užitečné pro krátkodobé vyrovnávací paměti, které se používají pouze v synchronním kódu (které nevyžadují Memory<T> instance), a také kód spuštěný v těsné smyčce, protože vytváření SpanOwner<T> hodnot vůbec nevyžaduje přidělení paměti.

Rozhraní API platformy:SpanOwner<T>, MemoryOwner<T>

Syntaxe

Stejné základní funkce MemoryOwner<T> platí i pro tento typ, s výjimkou, že je pouze structzásobník , a skutečnost, že chybí IMemoryOwner<T>interface implementace, stejně jako Memory<T> vlastnost. Syntaxe je prakticky identická s tím MemoryOwner<T> , s výjimkou výše uvedených rozdílů.

Předpokládejme například, že máme metodu, kdy potřebujeme přidělit dočasnou vyrovnávací paměť zadané velikosti (pojďme tuto hodnotu lengthvolat) a pak ji použít k provedení nějaké práce. První neefektivní verze může vypadat takto:

byte[] buffer = new byte[length];

// Use buffer here

To není ideální, protože při každém použití tohoto kódu přidělujeme novou vyrovnávací paměť a okamžitě ji zahodíme (jak je uvedeno také v MemoryOwner<T> dokumentaci), což klade větší tlak na uvolňování paměti. Výše uvedený kód můžeme optimalizovat pomocí 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);
}

Výše uvedený kód pronajímá vyrovnávací paměť z fondu polí, ale je náchylnější a náchylnější k chybám: musíme být opatrní s try/finally blokem, abychom vždy vrátili zapůjčení vyrovnávací paměti do fondu. Můžeme ho SpanOwner<T> přepsat pomocí typu, například takto:

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

Instance SpanOwner<T> interně pronajímá pole a postará se o jeho vrácení do fondu, jakmile bude mimo rozsah. Už nemusíme používat try/finally blok, protože kompilátor jazyka C# přidá tento blok automaticky při rozbalení daného using příkazu. Jako takový SpanOwner<T> lze typ považovat za jednoduchý obálka kolem ArrayPool<T> rozhraní API, což zkomprimuje a usnadňuje jejich použití, což snižuje množství kódu, který je potřeba zapsat k řádnému pronájmu a odstranění krátkodobých vyrovnávacích pamětí. Uvidíte, jak použití SpanOwner<T> kódu výrazně zkracuje a zjednodušuje.

Poznámka:

Vzhledem k tomu, že se jedná o typ jen pro zásobník, spoléhá na vzor typu kachního typu zavedený IDisposable v jazyce C# 8. To je znázorněno v ukázce výše: SpanOwner<T> typ se používá v using bloku navzdory tomu, že typ vůbec neimplementuje IDisposable rozhraní a není nikdy boxován. Funkce je stejná: jakmile vyrovnávací paměť zmizí z rozsahu, automaticky se odstraní. Rozhraní API SpanOwner{T} spoléhají na tento vzor pro zvýšení výkonu: předpokládají, že základní vyrovnávací paměť nebude nikdy uvolněna, pokud SpanOwner<T> je typ v oboru, a neprovádějí další kontroly, které jsou provedeny MemoryOwner<T> , aby se zajistilo, že vyrovnávací paměť je ve skutečnosti stále k dispozici před vrácením Memory<T> instance nebo Span<T> z ní. Tento typ by měl být vždy použit s blokem nebo výrazem using . Tím se nevrátí základní vyrovnávací paměť do sdíleného fondu. Technicky totéž lze dosáhnout také ručním voláním Dispose typu SpanOwner<T> (který nevyžaduje C# 8), ale to je náchylné k chybám, a proto se nedoporučuje.

Příklady

Další příklady najdete v testech jednotek.