Používání Úkolů nebo tříd Úkol má výkonnostní úzké hrdlo, které jsme v předchozích článcích nezmiňovali. Stručně řečeno, tyto kurzy vedou, když jsou výsledky okamžitě dostupnéZbytečné přidělování。 To znamená, že nový Úkol nebo objekt Úkol bude vždy vytvořen, i když je výsledek již dostupný. Zmínili jsme, že koncept async/await, který jsme použili v předchozích článcích, existuje už od vydání .NET 4.5. Tato funkce byla od verze C# 7 rozšířena o .NET 4.7 s pomocí struktury ValueTask, která může být použita jako návrat pro asynchronní funkce.
Struktura ValueTasku
Struktura ValueTask se poprvé objevila v repozitáři corefxlab v roce 2015. Tento repozitář slouží k experimentování a zkoumání nových nápadů, které se možná nedostanou do hlavního corefx repozitáře. Repozitář Corefx je repozitář, kde se nacházejí všechny základní knihovny .NET Core. Vyvinul a navrhl ji Stephen Taub pro knihovnu System.Threading.Tasks.Channels. Tehdy Stephen poskytl stručné vysvětlení:
Adresa knihovny corefxlab:Přihlášení k hypertextovému odkazu je viditelné.
ValueTask je samostatné sjednocení T a Tasku, které umožňuje ReadAsync volně alokovat a synchronně vracet dostupné hodnoty T (na rozdíl od použití Task.FromResult, které vyžaduje alokaci instance Task). ValueTask je odkladatelný, takže spotřeba většiny instancí je nerozeznatelná od spotřeby úkolů. Mnoho lidí vidí výhody této struktury, která je součástí balíčku System.Threading.Tasks.Extensions NuGet, která je součástí C# 7. Než se tedy ponoříme do struktury ValueTask, pojďme se podívat na problém, který používá k řešení. Protože Task(Task) je referenční typ, začněte sAsynchronní metoda vracející objekt Task znamená, že je vždy alokován na haldě。 To je v mnoha případech nezbytné.
Nicméně v některých případech asynchronní metody vracejí výsledky okamžitě nebo synchronně kompletně. V těchto případech je toto přidělení zbytečné a může být nákladné v kritických částech kódu. Až do vydání .NET 4.7 nebylo možné tomu zabránit, protože asynchronní metody musely vracet Task, Task <T>nebo void (poslední možnost byla obvykle nežádoucí). V této verzi .NET je toto rozšířené, což znamená, že asynchronní metoda může vracet jakýkoli typ, pokud má přístupnou metodu GetAwaiter. ValueTask je konkrétním příkladem tohoto typu a byl také přidán do této verze.
Můžete si prohlédnout repozitář corefx a vidět kompletní implementaci ValueTask, zde je sekce API, která nás zajímá:
Jako struktura umožňuje ValueTask psát asynchronní metody, které během synchronního běhu nealokují paměť. Konzistence API konceptu async/await tímto způsobem není narušena. Kromě toho tato struktura čeká samostatně, což ji činí snadno použitelnou. Například pokud spustíme tento jednoduchý kód:
V metodě MultiplyAsync simulujeme situaci, kdy chceme vyhnout se použití Task a vrátit pouze jednoduché celé číslo. To se provádí v příkazu if metody, kde v podstatě kontrolujeme, zda je předaný parametr nulový. Problém jeI kdyby naše podmínka ve fif příkazu platila, kód výše vytvořený vytváří objekt Task。 Tento problém řešíme takto:
ValueTask a Task
Jak již bylo zmíněno, existují dvě hlavní výhody používání ValueTask:
- Zlepšení výkonu
- Zvýšení flexibility implementace
Jaká jsou tedy čísla za těmi zlepšeními výkonu? Sledujte tento kód:
Pokud spustíme tento kód, trvá 120 ns na spuštění JIT. Pokud nahradíme Úkol Hodnotovým úkolem takto:
S JIT dosáhneme doby provedení 65 ns. Je pravda, že kvůli Task.Delay neprovádíme synchronně, ale vidíme zlepšení doby vykonání.
Další výhodou, kterou jsme zmínili, je větší flexibilita při implementaci. Co to vlastně znamená? Implementace asynchronních rozhraní, která by měla být synchronizována, budou nucena používat Task.Run nebo Task.FromResult. Samozřejmě to vede k problémům s výkonem, o kterých jsme mluvili dříve. Když používáme ValueTask, je pravděpodobnější, že si vybereme mezi synchronními nebo asynchronními implementacemi. Mějte na paměti, že to může být znamení, že váš kód nemusí být dobře navržený, pokud se vám to stane.
Například si všimněte tohoto rozhraní:
Řekněme, že ho chcete volat podle kódu takto:
Protože v rozhraní používáme ValueTask, může být implementace rozhraní synchronní nebo asynchronní. Tuto výhodu můžeme získat tím, že v podstatě přeskočíme přidání některých funkcí do IThingu, které řeší synchronizační chování. Tímto způsobem je mnohem jednodušší toto rozhraní používat. Zde je synchronní implementace výše uvedeného rozhraní:
Tady je asynchronní implementace stejného rozhraní:
Nicméně musíme zvážit některé kompromisy před použitím ValueTask. Je snadné si myslet, že by se ValueTask měl používat ve výchozím nastavení místo Task, což rozhodně není pravda. Například ačkoliv nám ValueTask pomáhá vyhnout se zbytečným přiřazením, když je k dispozici synchronizace výsledků, obsahuje také dvě pole.
Je důležité si uvědomit, že tuto strukturu zde používáme, což znamená, že používáme typy hodnot a všechny jejich břemena. Úkol je naopak referenční typ s jediným polím.Když používáte ValueTask, máme více dat k zpracování a zpracování. Pokud se taková metoda čeká v asynchronní metodě, pak je asynchronní metodaStavový automat bude také větší, protože ukládání celé struktury obvykle vyžaduje více místa než ukládání jedné reference.
Proto lidé v Microsoftu skutečně doporučují používat Task nebo Task jako výchozí typ návratu pro asynchronní metody. Teprve po analýze výkonu byste měli zvážit přechod na ValueTask.
shrnutí
ValueTask je struktura zavedená v .NET 4.7, která nám dává mnoho možností používat asynchronní metody v .NET. Nicméně to není bez ceny. To je užitečné pro výkonově kritické metody, které jsou prováděny synchronně. S nimi se vyhneme přiřazování zbytečných předmětů. Přesto jako hodnotový typ přináší všechny problémy, které hodnotové typy obvykle mají. Proto můžeme z této struktury těžit, ale musíme být opatrní.
Původní adresa:Přihlášení k hypertextovému odkazu je viditelné.
|