Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Med hjälp Task.WhenAnyav kan du starta flera uppgifter samtidigt och bearbeta dem en i taget när de har slutförts i stället för att bearbeta dem i den ordning de startas.
I följande exempel används en fråga för att skapa en samling uppgifter. Varje uppgift laddar ned innehållet på en angiven webbplats. I varje iteration av en while-loop returnerar ett inväntat anrop för att WhenAny returnera uppgiften i samlingen med uppgifter som slutför nedladdningen först. Den uppgiften tas bort från samlingen och bearbetas. Loopen upprepas tills samlingen inte innehåller några fler uppgifter.
Förutsättningar
Du kan följa den här självstudien med något av följande alternativ:
- Visual Studio 2022 med .NET desktop-utvecklingsarbetsflödet installerat. .NET SDK installeras automatiskt när du väljer den här arbetsbelastningen.
- .NET SDK med valfri kodredigerare, till exempel Visual Studio Code.
Skapa exempelprogram
Skapa ett nytt .NET Core-konsolprogram. Du kan skapa ett med hjälp av det nya dotnet-konsolkommandot eller från Visual Studio.
Öppna filen Program.cs i kodredigeraren och ersätt den befintliga koden med den här koden:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Lägg till fält
Program Lägg till följande två fält i klassdefinitionen:
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
static readonly IEnumerable<string> s_urlList = new string[]
{
"https://free.blessedness.top",
"https://free.blessedness.top/aspnet/core",
"https://free.blessedness.top/azure",
"https://free.blessedness.top/azure/devops",
"https://free.blessedness.top/dotnet",
"https://free.blessedness.top/dynamics365",
"https://free.blessedness.top/education",
"https://free.blessedness.top/enterprise-mobility-security",
"https://free.blessedness.top/gaming",
"https://free.blessedness.top/graph",
"https://free.blessedness.top/microsoft-365",
"https://free.blessedness.top/office",
"https://free.blessedness.top/powershell",
"https://free.blessedness.top/sql",
"https://free.blessedness.top/surface",
"https://free.blessedness.top/system-center",
"https://free.blessedness.top/visualstudio",
"https://free.blessedness.top/windows",
"https://free.blessedness.top/maui"
};
Gör HttpClient det möjligt att skicka HTTP-begäranden och ta emot HTTP-svar. Innehåller s_urlList alla URL:er som programmet planerar att bearbeta.
Uppdatera programmets startpunkt
Den viktigaste startpunkten i konsolprogrammet är Main metoden. Ersätt den befintliga metoden med följande:
static Task Main() => SumPageSizesAsync();
Den uppdaterade Main metoden betraktas nu som en Async-huvud, vilket möjliggör en asynkron startpunkt i den körbara filen. Det uttrycks som ett anrop till SumPageSizesAsync.
Skapa metoden för asynkrona sum-sidstorlekar
Main Lägg till SumPageSizesAsync metoden under metoden:
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");
}
Loopen while tar bort en av uppgifterna i varje iteration. När varje uppgift har slutförts avslutas loopen. Metoden börjar med att instansiera och starta en Stopwatch. Den innehåller sedan en fråga som, när den körs, skapar en samling uppgifter. Varje anrop till ProcessUrlAsync i följande kod returnerar ett Task<TResult>, där TResult är ett heltal:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
På grund av uppskjuten körning med LINQ anropar Enumerable.ToList du för att starta varje uppgift.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Loopen while utför följande steg för varje uppgift i samlingen:
Väntar på ett anrop till för att
WhenAnyidentifiera den första uppgiften i samlingen som har slutfört nedladdningen.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Tar bort uppgiften från samlingen.
downloadTasks.Remove(finishedTask);Awaits
finishedTask, som returneras av ett anrop tillProcessUrlAsync. VariabelnfinishedTaskär en Task<TResult> varTResultär ett heltal. Uppgiften är redan klar, men du väntar på att den ska hämta längden på den nedladdade webbplatsen, vilket visas i följande exempel. Om uppgiften är felaktigawaitutlöser det första underordnade undantaget som lagras iAggregateException, till skillnad från att läsa Task<TResult>.Result egenskapen, vilket skulle utlösaAggregateException.total += await finishedTask;
Lägg till processmetod
Lägg till följande ProcessUrlAsync metod under SumPageSizesAsync metoden:
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;
}
För en viss URL använder metoden den angivna instansen client för att hämta svaret som en byte[]. Längden returneras när URL:en och längden har skrivits till konsolen.
Kör programmet flera gånger för att kontrollera att de nedladdade längderna inte alltid visas i samma ordning.
Försiktighet
Du kan använda WhenAny i en loop, enligt beskrivningen i exemplet, för att lösa problem som omfattar ett litet antal uppgifter. Andra metoder är dock mer effektiva om du har ett stort antal uppgifter att bearbeta. Mer information och exempel finns i Bearbeta uppgifter när de slutförs.
Förenkla metoden med hjälp av Task.WhenEach
Loopen while som implementeras i SumPageSizesAsync metoden kan förenklas med den nya Task.WhenEach metoden som introducerades i .NET 9 genom att anropa den i await foreach loop.
Ersätt den tidigare implementerade while loopen:
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
med den förenklade await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Med den här nya metoden kan du inte längre anropa Task.WhenAny flera gånger för att manuellt anropa en uppgift och ta bort den som har slutförts, eftersom Task.WhenEach den itererar genom uppgiften i rätt ordning.
Fullständigt exempel
Följande kod är den fullständiga texten i Program.cs-filen för exemplet.
using System.Diagnostics;
HttpClient s_client = new()
{
MaxResponseContentBufferSize = 1_000_000
};
IEnumerable<string> s_urlList = new string[]
{
"https://free.blessedness.top",
"https://free.blessedness.top/aspnet/core",
"https://free.blessedness.top/azure",
"https://free.blessedness.top/azure/devops",
"https://free.blessedness.top/dotnet",
"https://free.blessedness.top/dynamics365",
"https://free.blessedness.top/education",
"https://free.blessedness.top/enterprise-mobility-security",
"https://free.blessedness.top/gaming",
"https://free.blessedness.top/graph",
"https://free.blessedness.top/microsoft-365",
"https://free.blessedness.top/office",
"https://free.blessedness.top/powershell",
"https://free.blessedness.top/sql",
"https://free.blessedness.top/surface",
"https://free.blessedness.top/system-center",
"https://free.blessedness.top/visualstudio",
"https://free.blessedness.top/windows",
"https://free.blessedness.top/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://free.blessedness.top 132,517
// https://free.blessedness.top/powershell 57,375
// https://free.blessedness.top/gaming 33,549
// https://free.blessedness.top/aspnet/core 88,714
// https://free.blessedness.top/surface 39,840
// https://free.blessedness.top/enterprise-mobility-security 30,903
// https://free.blessedness.top/microsoft-365 67,867
// https://free.blessedness.top/windows 26,816
// https://free.blessedness.top/maui 57,958
// https://free.blessedness.top/dotnet 78,706
// https://free.blessedness.top/graph 48,277
// https://free.blessedness.top/dynamics365 49,042
// https://free.blessedness.top/office 67,867
// https://free.blessedness.top/system-center 42,887
// https://free.blessedness.top/education 38,636
// https://free.blessedness.top/azure 421,663
// https://free.blessedness.top/visualstudio 30,925
// https://free.blessedness.top/sql 54,608
// https://free.blessedness.top/azure/devops 86,034
// Total bytes returned: 1,454,184
// Elapsed time: 00:00:01.1290403