Peristiwa
17 Mac, 9 PTG - 21 Mac, 10 PG
Sertai siri perjumpaan untuk membina penyelesaian AI berskala berdasarkan kes penggunaan dunia sebenar dengan rakan pembangun dan pakar.
Daftar sekarangPelayar ini tidak lagi disokong.
Naik taraf kepada Microsoft Edge untuk memanfaatkan ciri, kemas kini keselamatan dan sokongan teknikal yang terkini.
By using Task.WhenAny, you can start multiple tasks at the same time and process them one by one as they're completed rather than process them in the order in which they're started.
The following example uses a query to create a collection of tasks. Each task downloads the contents of a specified website. In each iteration of a while loop, an awaited call to WhenAny returns the task in the collection of tasks that finishes its download first. That task is removed from the collection and processed. The loop repeats until the collection contains no more tasks.
You can follow this tutorial by using one of the following options:
Create a new .NET Core console application. You can create one by using the dotnet new console command or from Visual Studio.
Open the Program.cs file in your code editor, and replace the existing code with this code:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
In the Program
class definition, add the following two fields:
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"
};
The HttpClient
exposes the ability to send HTTP requests and receive HTTP responses. The s_urlList
holds all of the URLs that the application plans to process.
The main entry point into the console application is the Main
method. Replace the existing method with the following:
static Task Main() => SumPageSizesAsync();
The updated Main
method is now considered an Async main, which allows for an asynchronous entry point into the executable. It is expressed as a call to SumPageSizesAsync
.
Below the Main
method, add the SumPageSizesAsync
method:
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");
}
The while
loop removes one of the tasks in each iteration. After every task has completed, the loop ends. The method starts by instantiating and starting a Stopwatch. It then includes a query that, when executed, creates a collection of tasks. Each call to ProcessUrlAsync
in the following code returns a Task<TResult>, where TResult
is an integer:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Due to deferred execution with the LINQ, you call Enumerable.ToList to start each task.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
The while
loop performs the following steps for each task in the collection:
Awaits a call to WhenAny
to identify the first task in the collection that has finished its download.
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
Removes that task from the collection.
downloadTasks.Remove(finishedTask);
Awaits finishedTask
, which is returned by a call to ProcessUrlAsync
. The finishedTask
variable is a Task<TResult> where TResult
is an integer. The task is already complete, but you await it to retrieve the length of the downloaded website, as the following example shows. If the task is faulted, await
will throw the first child exception stored in the AggregateException
, unlike reading the Task<TResult>.Result property, which would throw the AggregateException
.
total += await finishedTask;
Add the following ProcessUrlAsync
method below the SumPageSizesAsync
method:
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;
}
For any given URL, the method will use the client
instance provided to get the response as a byte[]
. The length is returned after the URL and length is written to the console.
Run the program several times to verify that the downloaded lengths don't always appear in the same order.
Awas
You can use WhenAny
in a loop, as described in the example, to solve problems that involve a small number of tasks. However, other approaches are more efficient if you have a large number of tasks to process. For more information and examples, see Processing tasks as they complete.
The following code is the complete text of the Program.cs file for the example.
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
.NET maklum balas
.NET ialah projek sumber terbuka. Pilih pautan untuk memberikan maklum balas:
Peristiwa
17 Mac, 9 PTG - 21 Mac, 10 PG
Sertai siri perjumpaan untuk membina penyelesaian AI berskala berdasarkan kes penggunaan dunia sebenar dengan rakan pembangun dan pakar.
Daftar sekarangLatihan
Laluan pembelajaran
Build distributed apps with .NET Aspire - Training
Learn how to build observable, production ready, distributed applications with .NET Aspire. AZ-2009
Dokumentasi
The Task Asynchronous Programming (TAP) model with async and await" - C#
Learn when and how to use Task-based async programming, a simplified approach to asynchronous programming in C#.
Learn about the return types that async methods can have in C# with code examples for each type.
Cancel async tasks after a period of time" - C#
Learn how to schedule cancellation of any associated tasks that are not completed within a period of time.