Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Met behulp van Task.WhenAnykunt u meerdere taken tegelijk starten en ze één voor één verwerken terwijl ze zijn voltooid in plaats van ze te verwerken in de volgorde waarin ze worden gestart.
In het volgende voorbeeld wordt een query gebruikt om een verzameling taken te maken. Elke taak downloadt de inhoud van een opgegeven website. In elke iteratie van een tijdjelus retourneert een wachtende aanroep om de taak te WhenAny retourneren in de verzameling taken die het downloaden van de taak als eerste hebben voltooid. Deze taak wordt verwijderd uit de verzameling en verwerkt. De lus wordt herhaald totdat de verzameling geen taken meer bevat.
Vereiste voorwaarden
U kunt deze zelfstudie volgen met een van de volgende opties:
- Visual Studio 2022 met de .NET-desktopontwikkeling workload geïnstalleerd. De .NET SDK wordt automatisch geïnstalleerd wanneer u deze workload selecteert.
- De .NET SDK met een code-editor van uw keuze, zoals Visual Studio Code.
Voorbeeldtoepassing maken
Maak een nieuwe .NET Core-consoletoepassing. U kunt er een maken met behulp van de nieuwe dotnet-consoleopdracht of vanuit Visual Studio.
Open het Program.cs-bestand in de code-editor en vervang de bestaande code door deze code:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Velden toevoegen
Voeg in de Program klassedefinitie de volgende twee velden toe:
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"
};
De HttpClient mogelijkheid om HTTP-aanvragen te verzenden en HTTP-antwoorden te ontvangen. De s_urlList bevat alle URL's die door de toepassing moeten worden verwerkt.
Toepassingsinvoerpunt bijwerken
Het belangrijkste toegangspunt in de consoletoepassing is de Main methode. Vervang de bestaande methode door het volgende:
static Task Main() => SumPageSizesAsync();
De bijgewerkte Main methode wordt nu beschouwd als een Async-hoofd, waardoor een asynchroon toegangspunt in het uitvoerbare bestand mogelijk is. Het wordt uitgedrukt als een oproep naar SumPageSizesAsync.
De methode asynchrone som van paginaformaten maken
Voeg onder de Main methode de SumPageSizesAsync methode toe:
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");
}
Met while de lus wordt een van de taken in elke iteratie verwijderd. Nadat elke taak is voltooid, eindigt de lus. De methode begint met het instantiëren en starten van een Stopwatch. Het bevat vervolgens een query die, wanneer deze wordt uitgevoerd, een verzameling taken maakt. Elke aanroep naar ProcessUrlAsync in de volgende code retourneert een Task<TResult>, waarbij TResult een geheel getal is:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Als gevolg van de uitgestelde uitvoering met de LINQ roept Enumerable.ToList u aan om elke taak te starten.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
De while lus voert de volgende stappen uit voor elke taak in de verzameling:
Wacht op een aanroep om de eerste taak in de verzameling te
WhenAnyidentificeren die de download heeft voltooid.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Hiermee verwijdert u die taak uit de verzameling.
downloadTasks.Remove(finishedTask);Wacht op
finishedTask, die wordt geretourneerd door een oproep naarProcessUrlAsync. DefinishedTaskvariabele is een Task<TResult> geheelTResultgetal. De taak is al voltooid, maar u wacht erop om de lengte van de gedownloade website op te halen, zoals in het volgende voorbeeld wordt weergegeven. Als er een fout optreedt bij de taak,awaitwordt de eerste onderliggende uitzondering gegenereerd die is opgeslagen in deAggregateExceptioneigenschap, in tegenstelling tot het lezen van de Task<TResult>.Result eigenschap, waardoor deAggregateException.total += await finishedTask;
Procesmethode toevoegen
Voeg de volgende ProcessUrlAsync methode toe onder de SumPageSizesAsync methode:
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;
}
Voor een bepaalde URL gebruikt de methode het client exemplaar dat is opgegeven om het antwoord op te halen als een byte[]. De lengte wordt geretourneerd nadat de URL en lengte naar de console is geschreven.
Voer het programma meerdere keren uit om te controleren of de gedownloade lengten niet altijd in dezelfde volgorde worden weergegeven.
Waarschuwing
U kunt in een lus, zoals beschreven in het voorbeeld, gebruiken WhenAny om problemen op te lossen die betrekking hebben op een klein aantal taken. Andere benaderingen zijn echter efficiënter als u een groot aantal taken moet verwerken. Zie Taken verwerken terwijl ze zijn voltooid voor meer informatie en voorbeelden.
De benadering vereenvoudigen met behulp van Task.WhenEach
De while lus die in SumPageSizesAsync de methode is geïmplementeerd, kan worden vereenvoudigd met behulp van de nieuwe Task.WhenEach methode die is geïntroduceerd in .NET 9 door deze in await foreach lus aan te roepen.
Vervang de eerder geïmplementeerde while lus:
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
met de vereenvoudigde await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Met deze nieuwe aanpak kunt u niet langer herhaaldelijk een taak aanroepen Task.WhenAny en de taak verwijderen die is voltooid, omdat Task.WhenEach taken in een volgorde van voltooiing worden herhaald.
Volledig voorbeeld
De volgende code is de volledige tekst van het Program.cs-bestand voor het voorbeeld.
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