Uporaba razredov Task ali Task ima ozko grlo v zmogljivosti, ki ga nismo omenili v prejšnjih člankih. Na kratko, ti tečaji vodijo, ko so rezultati takoj na voljoNepotrebna razporeditev。 To pomeni, da bo vedno ustvarjena nova naloga ali objekt naloge, tudi če je rezultat že na voljo. Omenili smo, da je koncept async/await, ki smo ga uporabljali v prejšnjih člankih, prisoten že od izdaje .NET 4.5. Ta funkcija je bila izboljšana od C# 7 z različico .NET 4.7 z ValueTask strukturo, ki se lahko uporablja kot vračilo za asinhrone funkcije.
Struktura ValueTaska
Struktura ValueTask se je prvič pojavila v repozitoriju corefxlab leta 2015. Ta repozitorij se uporablja za eksperimentiranje in raziskovanje novih idej, ki morda ne pridejo v glavni corefx repozitorij. Corefx repozitorij je repozitorij, kjer se nahajajo vse osnovne .NET Core knjižnice. Razvil in predlagal ga je Stephen Taub za knjižnico System.Threading.Tasks.Channels. Takrat je Stephen podal kratko razlago:
Naslov knjižnice corefxlab:Prijava do hiperpovezave je vidna.
ValueTask je ločena združitev T in Task, ki omogoča ReadAsyncu prosto dodeljevanje za sinhrono vračanje razpoložljivih T vrednosti (za razliko od uporabe Task.FromResult, ki zahteva dodelitev instance Task). ValueTask je mogoče počakati, zato je poraba večine instanc nerazločljiva od porabe nalog. Veliko ljudi vidi prednosti uporabe te strukture, ki je vključena v C# 7 kot del paketa System.Threading.Tasks.Extensions NuGet. Preden se poglobimo v strukturo ValueTask, si poglejmo problem, ki ga uporablja za reševanje. Ker je Task(Task) referenčni tip, začni zAsinhrona metoda, ki vrača objekt Task, pomeni, da je ta vsakič dodeljen na kupu。 To je v mnogih primerih nujno.
Vendar pa v nekaterih primerih asinhrone metode takoj ali sinhrono vrnejo rezultate. V takih primerih ta dodelitev ni potrebna in lahko postane draga v delih kode, ki so kritični za zmogljivost. Do izdaje .NET 4.7 tega ni bilo mogoče preprečiti, saj so asinhrone metode morale vrniti Nalogo, Nalogo <T>ali razveljavitev (zadnja možnost je bila običajno nezaželena). V tej različici .NET je to razširjeno, kar pomeni, da lahko asinhrona metoda vrne katerikoli tip, dokler ima dostopno metodo GetAwaiter. ValueTask je konkreten primer te vrste in je bil prav tako dodan tej izdaji.
Lahko brskate po repozitoriju corefx in si ogledate celotno implementacijo ValueTask, tukaj je API razdelek, ki nas zanima:
Kot struktura ValueTask omogoča pisanje asinhronih metod, ki med sinhronim izvajanjem ne dodeljujejo pomnilnika. Skladnost API koncepta async/await ni na ta način ogrožena. Poleg tega ta struktura čaka samostojno, kar jo naredi enostavno za uporabo. Na primer, če zaženemo to preprosto kodo:
V metodi MultiplyAsync simuliramo situacijo, ko želimo preprečiti uporabo naloge in vrniti le preprosto celo število. To se izvede v stavku if metode, kjer v bistvu preverjamo, ali je preneseni parameter enak nič. Težava jeTudi če je naš pogoj v stavku if resničen, zgornja koda ustvari objekt Task。 Ta problem rešimo takole:
ValueTask in Naloga
Kot je bilo že omenjeno, obstajata dve glavni prednosti uporabe ValueTask:
- Izboljšave zmogljivosti
- Povečajte prilagodljivost implementacije
Torej, kakšne so številke za izboljšave zmogljivosti? Opazujte to kodo:
Če zaženemo to kodo, traja 120 ns za izvedbo JIT-a. Če Nalogo zamenjamo z ValueTask takole:
Z JIT bomo dosegli čas izvedbe 65ns. Res je, da zaradi Task.Delay ne izvajamo sinhrono, vendar opažamo izboljšanje časa izvajanja.
Druga prednost, ki smo jo omenili, je večja prilagodljivost pri izvajanju. Kaj točno to pomeni? No, implementacije asinhronih vmesnikov, ki bi morale biti sinhronizirane, bodo prisiljene uporabljati Task.Run ali Task.FromResult. Seveda to vodi do težav z zmogljivostjo, o katerih smo govorili prej. Ko uporabljamo ValueTask, bomo bolj verjetno izbirali med sinhronimi ali asinhronimi implementacijami. Upoštevajte, da je to lahko znak, da vaša koda morda ni dobro zasnovana, če se vam to zgodi.
Na primer, opazujte ta vmesnik:
Recimo, da želite klicati iz kode takole:
Ker v vmesniku uporabljamo ValueTask, je implementacija vmesnika lahko sinhrona ali asinhrona. To prednost lahko dosežemo tako, da v IThing preprosto preskočimo dodajanje nekaterih funkcij, ki upravljajo sinhronizacijo. Ta vmesnik je veliko lažje uporabljati na ta način. Tukaj je sinhrona implementacija zgornjega vmesnika:
Tukaj je asinhrona implementacija istega vmesnika:
Vendar pa moramo pred uporabo ValueTask upoštevati nekatere kompromise. Lahko je misliti, da bi morali privzeto uporabljati ValueTask namesto Task, kar zagotovo ni res. Na primer, čeprav nam ValueTask pomaga preprečiti nepotrebne dodelitve, kadar je na voljo sinhronizacija rezultatov, vsebuje tudi dve polji.
Pomembno je vedeti, da uporabljamo to strukturo, kar pomeni, da uporabljamo vrste vrednosti in vse njihove bremena. Naloga pa je referenčni tip z le enim poljem.Ko uporabljate ValueTask, imamo več podatkov za obdelavo in obdelavo. Če se na takšno metodo čaka v asinhroni metodi, potem je asinhrona metodaTudi državni stroj bo večji, ker shranjevanje celotne strukture običajno zahteva več prostora kot shranjevanje ene same reference.
Zato ljudje pri Microsoftu dejansko priporočajo uporabo Naloge ali Naloge kot privzete vrste vrnitve za asinhrone metode. Šele po analizi zmogljivosti razmislite o prehodu na ValueTask.
Povzetek
ValueTask je struktura, uvedena v .NET 4.7, ki nam ponuja veliko možnosti za uporabo asinhronih metod v .NET. Vendar pa to ni brez cene. To je uporabno za metode, kritične za zmogljivost, ki se izvajajo sinhrono. Z njimi se lahko izognemo dodeljevanju nepotrebnih predmetov. Kljub temu pa kot vrednostni tip prinaša vse težave, ki jih vrednostni tipi običajno imajo. Zato lahko od te strukture imamo koristi, vendar moramo biti previdni.
Izvirni naslov:Prijava do hiperpovezave je vidna.
|