Folosirea claselor Task sau Task are un blocaj de performanță pe care nu l-am menționat în articolele anterioare. Pe scurt, aceste cursuri conduc atunci când rezultatele sunt imediat disponibileAlocare inutilă。 Aceasta înseamnă că un nou Task sau un obiect Task va fi întotdeauna creat chiar dacă rezultatul este deja disponibil. Acum, am menționat că conceptul async/await folosit în articolele anterioare există încă de la lansarea .NET 4.5. Această funcție a fost îmbunătățită încă din C#7 cu versiunea .NET 4.7, cu structura ValueTask, care poate fi folosită ca retur pentru funcții asincrone.
Structura ValueTask
Structura ValueTask a apărut pentru prima dată în depozitul corefxlab în 2015. Acest depozit este folosit pentru a experimenta și explora idei noi care pot sau nu să ajungă în depozitul principal corefx. Depozitul Corefx este depozitul unde se află toate bibliotecile de bază .NET Core. A fost dezvoltat și sugerat de Stephen Taub pentru biblioteca System.Threading.Tasks.Channels. La acel moment, Stephen a oferit o scurtă explicație:
Adresa bibliotecii CorefxLab:Autentificarea cu hyperlink este vizibilă.
Un ValueTask este o uniune distinctă între un T și un Task, permițând ReadAsync să aloce liber pentru a returna sincron valorile T disponibile (spre deosebire de utilizarea Task.FromResult, care necesită alocarea unei instanțe Task). ValueTask este așteptabil, astfel încât consumul majorității instanțelor este indistinct de consumul sarcinilor. Mulți oameni văd beneficiile utilizării acestei structuri, care este inclusă în C# 7 ca parte a pachetului NuGet System.Threading.Tasks.Extensions. Așadar, înainte de a intra în structura ValueTask, să analizăm problema pe care o folosește pentru a o rezolva. Deoarece Task(Task) este un tip de referință, începe cuMetoda asincronă care returnează obiectul Task înseamnă că acesta este alocat pe heap de fiecare dată。 Acest lucru este necesar în multe cazuri.
Totuși, în unele cazuri, metodele asincrone returnează rezultate imediat sau complet sincron. În aceste cazuri, această alocare este inutilă și poate deveni costisitoare în părțile critice de performanță ale codului. Până la lansarea .NET 4.7, nu exista nicio cale de a evita acest lucru, deoarece metodele asincrone trebuiau să returneze Task, Task <T>sau void (ultimul era de obicei nedorit). În această versiune a .NET, aceasta este extinsă, ceea ce înseamnă că o metodă asincronă poate returna orice tip atâta timp cât are o metodă GetAwaiter accesibilă. ValueTask este un exemplu concret al acestui tip și a fost adăugat și în această versiune.
Puteți răsfoi depozitul corefx și vedea implementarea completă a ValueTask, iată secțiunea API care ne interesează:
Ca structură, ValueTask permite scrierea unor metode asincrone care nu alocă memorie în timpul runtimei sincrone. Consistența API a conceptului async/await nu este compromisă în acest mod. În plus, această structură așteaptă de una singură, făcând-o ușor de folosit. De exemplu, dacă rulăm acest cod simplu:
În metoda MultiplyAsync, simulăm o situație în care vrem să evităm folosirea Sarcinii și să returnăm doar un număr întreg simplu. Acest lucru se face în instrucțiunea if a metodei, unde practic verificăm dacă parametrul trecut este zero. Problema esteChiar dacă condiția noastră din instrucțiunea if este adevărată, codul de mai sus creează un obiect Task。 Rezolvăm această problemă astfel:
ValueTask și Task
După cum s-a menționat anterior, există două beneficii principale în utilizarea ValueTask:
- Îmbunătățiri de performanță
- Creșterea flexibilității implementării
Deci, care sunt cifrele din spatele îmbunătățirilor de performanță? Observați acest cod:
Dacă rulăm acest cod, durează 120ns pentru a executa JIT-ul. Acum, dacă înlocuim Task cu ValueTask astfel:
Cu JIT, vom obține un timp de execuție de 65ns. Este adevărat că, din cauza Task.Delay, nu executăm sincron, dar vedem o îmbunătățire a timpului de execuție.
Un alt beneficiu menționat este flexibilitatea crescută în implementare. Acum, ce înseamnă exact asta? Ei bine, implementările interfețelor asincrone care ar trebui sincronizate vor fi obligate să folosească Task.Run sau Task.FromResult. Desigur, acest lucru duce la problemele de performanță despre care am discutat anterior. Când folosim ValueTask, este mai probabil să alegem între implementări sincrone sau asincrone. Ține cont că acest lucru ar putea fi un semn că codul tău s-ar putea să nu fie bine conceput dacă ți se întâmplă acest lucru.
De exemplu, observați această interfață:
Să zicem că vrei să-l numești dintr-un cod ca acesta:
Pentru că folosim ValueTask în interfață, implementarea interfeței poate fi sincronă sau asincronă. Putem obține acest beneficiu practic sărind peste adăugarea unor funcții în IT care gestionează comportamentul sincronizării. Este mult mai ușor să folosești această interfață în acest mod. Iată o implementare sincronă a interfeței de mai sus:
Iată o implementare asincronă a aceleiași interfețe:
Totuși, trebuie să luăm în considerare unele compromisuri înainte de a folosi ValueTask. Este ușor de gândit că ValueTask ar trebui folosit implicit în locul Task, ceea ce cu siguranță nu este cazul. De exemplu, deși ValueTask ne ajută să evităm atribuirile inutile atunci când sincronizarea rezultatelor este disponibilă, conține și două câmpuri.
Este important să ne amintim că aceasta este structura pe care o folosim aici, ceea ce înseamnă că folosim tipurile de valoare și toate poverile lor. Sarcina, pe de altă parte, este un tip de referință cu un singur câmp.Când folosești ValueTask, avem mai multe date de procesat și procesat. Dacă o astfel de metodă este așteptată într-o metodă asincronă, atunci metoda asincronă esteMașina de stare va fi, de asemenea, mai mare, deoarece stocarea întregii structuri necesită de obicei mai mult spațiu decât stocarea unei singure referințe.
De aceea, cei de la Microsoft recomandă de fapt să folosești Task sau Task ca tip implicit de returnare pentru metodele asincrone. Abia după analiza performanței ar trebui să iei în considerare trecerea la ValueTask.
rezumat
ValueTask este o structură introdusă în .NET 4.7 care ne oferă multe posibilități de a folosi metode asincrone în .NET. Totuși, nu este lipsit de un preț. Acest lucru este util pentru metode critice de performanță care sunt executate sincron. Cu ele putem evita să atribuim obiecte inutile. Totuși, ca tip de valoare, vine cu toate problemele pe care le au de obicei tipurile de valoare. Prin urmare, putem beneficia de această structură, dar trebuie să fim atenți.
Adresa originală:Autentificarea cu hyperlink este vizibilă.
|