Brugen af Task eller Task-klasser har en performance-flaskehals, som vi ikke har nævnt i tidligere artikler. Kort sagt fører disse klasser, når resultaterne er tilgængelige med det sammeUnødvendig tildeling。 Det betyder, at et nyt Task eller Task-objekt altid vil blive oprettet, selvom resultatet allerede er tilgængeligt. Nu nævnte vi, at det asynkrone/await-koncept, vi brugte i tidligere artikler, har eksisteret siden .NET 4.5-udgivelsen. Denne funktion er blevet forbedret siden C# 7 med .NET 4.7-versionen med ValueTask-strukturen, som kan bruges som retur for asynkrone funktioner.
ValueTask-struktur
ValueTask-strukturen dukkede først op i corefxlab-repositoriet i 2015. Dette repository bruges til at eksperimentere og udforske nye idéer, som måske eller måske ikke når frem til hoved-corefx-repositoryet. Corefx-repositoryet er det repository, hvor alle .NET Core-basebibliotekerne er placeret. Det blev udviklet og foreslået af Stephen Taub til System.Threading.Tasks.Channels-biblioteket. På det tidspunkt gav Stephen en kort forklaring:
corefxlab biblioteksadresse:Hyperlink-login er synlig.
En ValueTask er en særskilt union af en T og en Task, hvilket gør det muligt for ReadAsync frit at allokere sine tilgængelige T-værdier for synkront at returnere (i modsætning til brugen af Task.FromResult, som kræver allokering af en Task-instans). ValueTask kan ventes, så forbruget af de fleste instanser er ikke til at skelne fra forbruget af opgaver. Mange ser fordelene ved at bruge denne struktur, som er inkluderet i C# 7 som en del af System.Threading.Tasks.Extensions NuGet-pakken. Så før vi dykker ned i ValueTask-strukturen, lad os se nærmere på det problem, den bruger til at løse. Da Task(Task) er en referencetype, start medDen asynkrone metode, der returnerer Task-objektet, betyder, at det tildeles på heapen hver gang。 Dette er nødvendigt i mange tilfælde.
Dog returnerer asynkrone metoder i nogle tilfælde resultater straks eller fuldfører synkront. I disse tilfælde er denne allokering unødvendig og kan blive dyr i ydelseskritiske dele af koden. Indtil .NET 4.7-udgivelsen var der ingen måde at undgå dette på, da asynkrone metoder skulle returnere Task, Task <T>eller void (den sidste var som regel uønsket). I denne version af .NET er dette udvidet, hvilket betyder, at en asynkron metode kan returnere enhver type, så længe den har en tilgængelig GetAwaiter-metode. ValueTask er et konkret eksempel på denne type, og det blev også tilføjet til denne udgivelse.
Du kan gennemse corefx-repositoryet og se den fulde implementering af ValueTask, her er API-sektionen, vi er interesserede i:
Som struktur tillader ValueTask skrivning af asynkrone metoder, der ikke allokerer hukommelse under synkron kørsel. API-konsistensen i asynkron/await-konceptet kompromitteres ikke på denne måde. Derudover venter denne struktur på sig selv, hvilket gør den nem at bruge. For eksempel, hvis vi kører denne simple kode:
I MultiplyAsync-metoden simulerer vi en situation, hvor vi ønsker at undgå at bruge Task og kun returnere et simpelt heltal. Dette gøres i metodens if-sætning, hvor vi grundlæggende tjekker, om den overleverede parameter er nul. Problemet erSelv hvis vores betingelse i if-sætningen er sand, opretter koden ovenfor et Task-objekt。 Vi løser dette problem sådan her:
ValueTask og Task
Som nævnt tidligere er der to hovedfordele ved at bruge ValueTask:
- Ydelsesforbedringer
- Øg implementeringsfleksibiliteten
Så, hvad er tallene bag ydelsesforbedringerne? Observer denne kode:
Hvis vi kører denne kode, tager det 120 ns at udføre JIT'en. Hvis vi nu erstatter Task med ValueTask sådan her:
Med JIT får vi en eksekveringstid på 65ns. Det er rigtigt, at vi på grund af Task.Delay ikke eksekverer synkront, men vi ser en forbedring i eksekveringstiden.
En anden fordel, vi nævnte, er den øgede fleksibilitet i implementeringen. Hvad betyder det egentlig? Implementeringer af asynkrone grænseflader, der bør synkroniseres, vil blive tvunget til at bruge Task.Run eller Task.FromResult. Selvfølgelig fører det til de præstationsproblemer, vi diskuterede tidligere. Når vi bruger ValueTask, er vi mere tilbøjelige til at vælge mellem synkrone eller asynkrone implementeringer. Husk, at dette kan være et tegn på, at din kode måske ikke er godt designet, hvis det sker for dig.
For eksempel, observer denne grænseflade:
Lad os sige, at du vil kalde det fra kode sådan her:
Fordi vi bruger ValueTask i grænsefladen, kan implementeringen af grænsefladen være synkron eller asynkron. Vi kan opnå denne fordel ved grundlæggende at springe over at tilføje nogle funktioner til IThing, der håndterer synkroniseringsadfærd. Det er meget nemmere at bruge dette interface på denne måde. Her er en synkron implementering af ovenstående grænseflade:
Her er en asynkron implementering af den samme grænseflade:
Vi må dog overveje nogle afvejninger, før vi bruger ValueTask. Det er let at tro, at ValueTask bør bruges som standard i stedet for Task, hvilket bestemt ikke er tilfældet. For eksempel, selvom ValueTask hjælper os med at undgå unødvendige tildelinger, når resultatsynkronisering er tilgængelig, indeholder det også to felter.
Det er vigtigt at huske, at det er denne struktur, vi bruger her, hvilket betyder, at vi bruger værdityper og alle deres byrder. Opgave er derimod en referencetype med kun ét felt.Når du bruger ValueTask, har vi mere data at behandle og behandle. Hvis en sådan metode afventes i en asynkron metode, så er den asynkrone metodeTilstandsmaskinen vil også være større, fordi lagring af hele strukturen normalt kræver mere plads end at gemme en enkelt reference.
Derfor anbefaler folkene hos Microsoft faktisk at bruge Task eller Task som standard returtype for asynkrone metoder. Først efter performance-analyse bør du overveje at skifte til ValueTask.
resumé
ValueTask er en struktur introduceret i .NET 4.7, som giver os mange muligheder for at bruge asynkrone metoder i .NET. Men det er ikke uden pris. Dette er nyttigt for performancekritiske metoder, der udføres synkront. Med dem kan vi undgå at tildele unødvendige genstande. Alligevel følger der som værditype alle de problemer, som værdityper normalt har. Derfor kan vi drage fordel af denne struktur, men vi må være forsigtige.
Original adresse:Hyperlink-login er synlig.
|