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 vrátí očekávané volání WhenAny úkolu v kolekci úkolů, které dokončí její stahování jako první. 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/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 asynchronní hlavní, což umožňuje asynchronní vstupní bod do spustitelného souboru. Vyjadřuje se jako volání SumPageSizesAsync.
Vytvoření metody asynchronních velikostí stránek součtu
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>hodnotu , kde TResult je celé číslo:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Vzhledem k odložené provádění pomocí LINQ voláte Enumerable.ToList , aby se spustily jednotlivé úlohy.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Smyčka while provádí pro každou úlohu v kolekci následující kroky:
Čeká volání k
WhenAnyidentifikaci prvního úkolu v kolekci, která dokončila stahování.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Odebere danou úlohu z kolekce.
downloadTasks.Remove(finishedTask);Awaits
finishedTask, který je vrácen volánímProcessUrlAsync. ProměnnáfinishedTaskje celé Task<TResult>TResultčí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 chybná,awaitvyvolá první podřízenou výjimku uloženou v objektuAggregateException, na rozdíl od čtení Task<TResult>.Result vlastnosti, která by vyvoláAggregateException.total += await finishedTask;
Přidání metody procesu
Pod metodu přidejte následující ProcessUrlAsync 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 získání odpovědi jako .byte[] Délka se vrátí za adresou URL a délkou se zapíše 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í
Ve smyčce, jak je popsáno v příkladu, můžete použít WhenAny 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 SumPageSizesAsync metodě lze zjednodušit voláním ve smyčce pomocí nové Task.WhenEach metody zavedené v await foreach .NET 9.
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 umožňuje už opakovaně volat Task.WhenAny úkol ručně a odebrat úkol, který se dokončí, protože Task.WhenEach iteruje úkol v pořadí jeho 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/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/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