Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Pomocí nich Task.WhenAnymůžete současně spouštět více úkolů a zpracovávat je po jednom, protože jsou dokončené, a ne je zpracovávat v pořadí, ve kterém jsou spuštěné.
Následující příklad používá dotaz k vytvoření kolekce úloh. Každý úkol stáhne obsah zadaného webu. V každé iteraci smyčky while zaznamená očekávané volání WhenAny úkol v kolekci úkolů, který jako první dokončí jeho stahování. Tato úloha se odebere z kolekce a zpracuje se. Smyčka se opakuje, dokud kolekce neobsahuje žádné další úkoly.
Požadavky
Můžete postupovat podle tohoto kurzu pomocí jedné z následujících možností:
- Visual Studio 2022 s nainstalovanou pracovní sadou pro vývoj desktopových aplikací .NET. Sada .NET SDK se automaticky nainstaluje při výběru této úlohy.
- Sada .NET SDK s editorem kódu podle vašeho výběru, jako je Visual Studio Code.
Vytvoření ukázkové aplikace
Vytvořte novou konzolovou aplikaci .NET Core. Můžete ho vytvořit pomocí příkazu dotnet new console nebo ze sady Visual Studio.
Otevřete soubor Program.cs v editoru kódu a nahraďte stávající kód tímto kódem:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Přidání polí
Program Do definice třídy přidejte následující dvě pole:
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
static readonly IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dynamics365",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://learn.microsoft.com/system-center",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/dotnet/maui"
};
Zpřístupňuje HttpClient schopnost odesílat požadavky HTTP a přijímat odpovědi HTTP. Obsahuje s_urlList všechny adresy URL, které aplikace plánuje zpracovat.
Aktualizace vstupního bodu aplikace
Hlavním vstupním bodem do konzolové aplikace je Main metoda. Nahraďte existující metodu následujícím kódem:
static Task Main() => SumPageSizesAsync();
Aktualizovaná Main metoda je nyní považována za Async Main, což umožňuje asynchronní vstupní bod pro spustitelný soubor. Je vyjádřeno jako vyvolání SumPageSizesAsync.
Vytvořit asynchronní metodu pro výpočet součtu velikostí stránek
Pod metodu Main přidejte metodu SumPageSizesAsync :
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
int total = 0;
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
Smyčka while odebere jednu z úloh v každé iteraci. Po dokončení každého úkolu se smyčka ukončí. Metoda začíná vytvořením instance a spuštěním Stopwatch. Pak obsahuje dotaz, který při spuštění vytvoří kolekci úloh. Každé volání ProcessUrlAsync v následujícím kódu vrátí Task<TResult>, kde TResult je celé číslo.
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Kvůli odloženému provádění pomocí LINQ voláte Enumerable.ToList, aby se spustila každá úloha.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Smyčka while provádí pro každou úlohu v kolekci následující kroky:
Čeká na volání funkce
WhenAny, aby identifikoval první úkol v kolekci, který dokončil stahování.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Odebere tuto úlohu z kolekce.
downloadTasks.Remove(finishedTask);Čeká na
finishedTask, který se obdrží zavolánímProcessUrlAsync. ProměnnáfinishedTaskje Task<TResult>, kdeTResultje celé číslo. Úkol je již dokončený, ale očekáváte, že načte délku staženého webu, jak ukazuje následující příklad. Pokud je úloha s chybou,awaitvyvolá první podřízenou výjimku uloženou v objektuAggregateException, na rozdíl od čtení vlastnosti Task<TResult>.Result, která by vyvolalaAggregateException.total += await finishedTask;
Přidání metody procesu
Přidejte následující metodu ProcessUrlAsync pod metodu SumPageSizesAsync.
static async Task<int> ProcessUrlAsync(string url, HttpClient client)
{
byte[] content = await client.GetByteArrayAsync(url);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
Pro každou danou adresu URL metoda použije client instanci poskytnutou k tomu, aby získala odpověď jako byte[]. Délka je vrácena poté, co adresa URL a délka jsou zapsány do konzoly.
Spusťte program několikrát a ověřte, že se stažené délky nezobrazují vždy ve stejném pořadí.
Upozornění
Můžete použít WhenAny ve smyčce, jak je popsáno v příkladu, k řešení problémů, které zahrnují malý počet úkolů. Pokud ale máte velký počet úkolů ke zpracování, jsou další přístupy efektivnější. Další informace a příklady najdete v tématu Zpracování úloh při jejich dokončení.
Zjednodušení přístupu pomocí Task.WhenEach
Smyčku while implementovanou v metodě SumPageSizesAsync lze zjednodušit pomocí nové metody Task.WhenEach zavedené v .NET 9, voláním v rámci smyčky await foreach.
Nahraďte dříve implementovanou while smyčku:
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
zjednodušeným await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Tento nový přístup již nevyžaduje opakované ruční volání Task.WhenAny úkolů a jejich odebrání po dokončení, protože Task.WhenEach iteruje skrz úkoly v pořadí jejich dokončení.
Kompletní příklad
Následující kód je úplný text souboru Program.cs příkladu.
using System.Diagnostics;
HttpClient s_client = new()
{
MaxResponseContentBufferSize = 1_000_000
};
IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dynamics365",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://learn.microsoft.com/system-center",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/dotnet/maui"
};
await SumPageSizesAsync();
async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
int total = 0;
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
static async Task<int> ProcessUrlAsync(string url, HttpClient client)
{
byte[] content = await client.GetByteArrayAsync(url);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
// Example output:
// https://learn.microsoft.com 132,517
// https://learn.microsoft.com/powershell 57,375
// https://learn.microsoft.com/gaming 33,549
// https://learn.microsoft.com/aspnet/core 88,714
// https://learn.microsoft.com/surface 39,840
// https://learn.microsoft.com/enterprise-mobility-security 30,903
// https://learn.microsoft.com/microsoft-365 67,867
// https://learn.microsoft.com/windows 26,816
// https://learn.microsoft.com/dotnet/maui 57,958
// https://learn.microsoft.com/dotnet 78,706
// https://learn.microsoft.com/graph 48,277
// https://learn.microsoft.com/dynamics365 49,042
// https://learn.microsoft.com/office 67,867
// https://learn.microsoft.com/system-center 42,887
// https://learn.microsoft.com/education 38,636
// https://learn.microsoft.com/azure 421,663
// https://learn.microsoft.com/visualstudio 30,925
// https://learn.microsoft.com/sql 54,608
// https://learn.microsoft.com/azure/devops 86,034
// Total bytes returned: 1,454,184
// Elapsed time: 00:00:01.1290403