Programmering

Hvornår skal man bruge Task.WaitAll vs. Task.WhenAll i .NET

TPL (Task Parallel Library) er en af ​​de mest interessante nye funktioner tilføjet i de nyere versioner af .NET framework. Task.WaitAll og Task.WhenAll-metoderne er to vigtige og ofte anvendte metoder i TPL.

Task.WaitAll blokerer den aktuelle tråd, indtil alle andre opgaver er gennemført. Metoden Task.WhenAll bruges til at oprette en opgave, der fuldføres, hvis og kun hvis alle de andre opgaver er afsluttet.

Så hvis du bruger Task.WhenAll får du et opgaveobjekt, der ikke er komplet. Det blokerer dog ikke, men tillader, at programmet køres. Tværtimod blokerer Task.WaitAll-metodekaldet faktisk og venter på, at alle andre opgaver skal udføres.

I det væsentlige giver Task.WhenAll dig en opgave, der ikke er afsluttet, men du kan bruge ContinueWith, så snart de angivne opgaver har gennemført deres udførelse. Bemærk, at hverken Task.WhenAll eller Task.WaitAll faktisk kører opgaverne; dvs. ingen opgaver startes med disse metoder. Sådan bruges ContinueWith med Task.WhenAll:

Task.WhenAll (taskList) .ContinueWith (t => {

// skriv din kode her

});

Som Microsofts dokumentation siger, skaber Task.WhenAll "en opgave, der afsluttes, når alle opgaveobjekterne i en utallig samling er afsluttet."

Task.WhenAll vs. Task.WaitAll

Lad mig forklare forskellen mellem disse to metoder med et simpelt eksempel. Antag at du har en opgave, der udfører en vis aktivitet med UI-tråden - sig, at der skal vises en animation i brugergrænsefladen. Hvis du nu bruger Task.WaitAll, blokeres brugergrænsefladen og opdateres ikke, før alle de relaterede opgaver er afsluttet, og blokken frigivet. Men hvis du bruger Task.WhenAll i samme applikation, vil UI-tråden ikke blive blokeret og vil blive opdateret som normalt.

Så hvilken af ​​disse metoder skal du bruge hvornår? Nå, du kan bruge WaitAll, når hensigten blokeres synkront for at få resultaterne. Men når du ønsker at udnytte asynkroni, skal du bruge WhenAll-varianten. Du kan afvente Task.WhenAll uden at skulle blokere den aktuelle tråd. Derfor vil du muligvis bruge wait med Task.WhenAll inde i en async-metode.

Mens Task.WaitAll blokerer den aktuelle tråd, indtil alle afventende opgaver er færdige, returnerer Task.WhenAll et opgaveobjekt. Task.WaitAll kaster en AggregateException, når en eller flere af opgaverne giver en undtagelse. Når en eller flere opgaver kaster en undtagelse, og du afventer Task.WhenAll-metoden, pakkes den ud AggregateException og returnerer kun den første.

Undgå at bruge Task.Kør i løkker

Du kan bruge opgaver, når du vil udføre samtidige aktiviteter. Hvis du har brug for en høj grad af parallelisme, er opgaver aldrig et godt valg. Det tilrådes altid at undgå at bruge trådpulstråde i ASP.Net. Derfor bør du afstå fra at bruge Task.Run eller Task.factory.StartNew i ASP.Net.

Task.Run skal altid bruges til CPU-bundet kode. Task.Run er ikke et godt valg i ASP.Net-applikationer eller applikationer, der udnytter ASP.Net-runtime, da det bare aflaster arbejdet til en ThreadPool-tråd. Hvis du bruger ASP.Net Web API, ville anmodningen allerede bruge en ThreadPool-tråd. Derfor, hvis du bruger Task.Run i din ASP.Net Web API-applikation, begrænser du bare skalerbarhed ved at aflæse arbejdet til en anden arbejdstråd uden nogen grund.

Bemærk, at der er en ulempe ved at bruge Task.Run i en loop. Hvis du bruger Task.Run-metoden i en løkke, oprettes der flere opgaver - en for hver enhed af arbejde eller iteration. Men hvis du bruger Parallel.ForEach i stedet for at bruge Task.Run inde i en løkke, oprettes en Partitioner for at undgå at oprette flere opgaver til at udføre aktiviteten, end den er nødvendig. Dette kan forbedre ydeevnen betydeligt, da du kan undgå for mange kontekstafbrydere og stadig udnytte flere kerner i dit system.

Det skal bemærkes, at Parallel.ForEach bruger Partitioner internt for at distribuere samlingen i arbejdsprodukter. I øvrigt sker denne distribution ikke for hver opgave på listen over varer, det sker snarere som en batch. Dette sænker de involverede omkostninger og forbedrer dermed ydeevnen. Med andre ord, hvis du bruger Task.Run eller Task.Factory.StartNew inde i en loop, ville de oprette nye opgaver eksplicit for hver iteration i loop. Parallel.ForEach er meget mere effektiv, fordi det vil optimere udførelsen ved at fordele arbejdsbelastningen på flere kerner i dit system.