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 struct
zá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 length
volat) 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
.NET Community Toolkit