Programování

Kdy použít Task.WaitAll vs. Task.WhenAll v .NET

TPL (Task Parallel Library) je jednou z nejzajímavějších nových funkcí přidaných v posledních verzích .NET framework. Metody Task.WaitAll a Task.WhenAll jsou dvě důležité a často používané metody v TPL.

Task.WaitAll blokuje aktuální vlákno, dokud nedokončí všechny ostatní úkoly. Task.WhenAll Metoda se používá k vytvoření úkolu, který se dokončí právě tehdy, pokud byly dokončeny všechny ostatní úkoly.

Takže pokud používáte Task.WhenAll, dostanete objekt úkolu, který není kompletní. Nebude však blokovat, ale umožní spuštění programu. Naopak volání metody Task.WaitAll ve skutečnosti blokuje a čeká na dokončení všech ostatních úkolů.

V zásadě vám Task.WhenAll dá úkol, který není dokončen, ale můžete pokračovat pomocí ContinueWith, jakmile zadané úkoly dokončí své provedení. Všimněte si, že Task.WhenAll ani Task.WaitAll ve skutečnosti úlohy nespustí; tj. těmito metodami nejsou spouštěny žádné úkoly. Takto se používá ContinuWith s Task.WhenAll:

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

// zde napište svůj kód

});

Jak uvádí dokumentace společnosti Microsoft, Task.WhenAll „vytvoří úkol, který se dokončí, až budou dokončeny všechny objekty Task ve vyčíslitelné kolekci.“

Task.WhenAll vs. Task.WaitAll

Dovolte mi vysvětlit rozdíl mezi těmito dvěma metodami na jednoduchém příkladu. Předpokládejme, že máte úkol, který provádí nějakou činnost s vláknem uživatelského rozhraní - řekněme, že v uživatelském rozhraní je třeba zobrazit nějakou animaci. Nyní, pokud používáte Task.WaitAll, bude uživatelské rozhraní blokováno a nebude aktualizováno, dokud nebudou dokončeny všechny související úkoly a blok nebude uvolněn. Pokud však používáte Task.WhenAll ve stejné aplikaci, vlákno uživatelského rozhraní nebude blokováno a bude aktualizováno jako obvykle.

Kterou z těchto metod byste tedy měli použít kdy? WaitAll můžete použít, když je záměr synchronně blokován, abyste získali výsledky. Ale pokud byste chtěli využít asynchronii, chtěli byste použít variantu WhenAll. Můžete čekat na Task.WhenAll, aniž byste museli blokovat aktuální vlákno. Proto možná budete chtít použít čekat s Task.WhenAll uvnitř asynchronní metody.

Zatímco Task.WaitAll blokuje aktuální vlákno, dokud nejsou dokončeny všechny nevyřízené úkoly, Task.WhenAll vrátí objekt úlohy. Task.WaitAll vyvolá AggregateException, když jedna nebo více úloh vyvolá výjimku. Když jedna nebo více úloh vyvolá výjimku a vy čekáte na Task.WhenAll metodu, rozbalí AggregateException a vrátí pouze první.

Nepoužívejte Task.Run ve smyčkách

Úkoly můžete použít, když chcete provádět souběžné aktivity. Pokud potřebujete vysoký stupeň paralelismu, úkoly nikdy nejsou dobrou volbou. Vždy se doporučuje vyhnout se používání vláken fondu vláken v ASP.Net. Proto byste se měli zdržet používání Task.Run nebo Task.factory.StartNew v ASP.Net.

Task.Run by měl být vždy použit pro vázaný kód CPU. Task.Run není dobrá volba v aplikacích ASP.Net, nebo v aplikacích, které využívají běhový modul ASP.Net, protože pouze uvolňuje práci do vlákna ThreadPool. Pokud používáte webové rozhraní API ASP.Net, požadavek by již používal vlákno ThreadPool. Proto pokud používáte Task.Run ve své aplikaci webového API ASP.Net, omezujete škálovatelnost pouze tím, že vyložíte práci na jiné pracovní vlákno bez ohledu na důvod.

Všimněte si, že při používání Task.Run ve smyčce existuje nevýhoda. Pokud použijete metodu Task.Run uvnitř smyčky, vytvoří se více úkolů - jeden pro každou jednotku práce nebo iteraci. Pokud však použijete Parallel.ForEach místo použití Task.Run uvnitř smyčky, vytvoří se Partitioner, aby se zabránilo vytváření dalších úkolů k provedení činnosti, než je potřeba. To může výrazně zlepšit výkon, protože se můžete vyhnout příliš mnoha kontextovým přepínačům a stále využívat více jader ve vašem systému.

Je třeba poznamenat, že Parallel.ForEach používá Partitioner interně k distribuci kolekce do pracovních položek. Mimochodem, k této distribuci nedochází pro každou úlohu v seznamu položek, ale spíše jako dávka. To snižuje režii a tím zvyšuje výkon. Jinými slovy, pokud používáte Task.Run nebo Task.Factory.StartNew uvnitř smyčky, vytvořili by nové úkoly explicitně pro každou iteraci ve smyčce. Parallel.ForEach je mnohem efektivnější, protože optimalizuje provádění distribucí pracovního zatížení mezi více jader ve vašem systému.

$config[zx-auto] not found$config[zx-overlay] not found