Aszinkron feladatok feldolgozása a befejezésük során (C#)
A használatával Task.WhenAnyegyszerre több tevékenységet is elindíthat, és egyenként feldolgozhatja őket, miközben befejeződnek, ahelyett, hogy a kezdési sorrendben dolgoznák fel őket.
Az alábbi példa egy lekérdezéssel hoz létre feladatgyűjteményt. Minden feladat letölti egy adott webhely tartalmát. Egy időhurok minden iterációjában egy várt hívás WhenAny adja vissza a feladatot a feladatgyűjteményben, amely először befejezi a letöltést. A rendszer eltávolítja a feladatot a gyűjteményből, és feldolgozzák. A hurok addig ismétlődik, amíg a gyűjtemény nem tartalmaz több feladatot.
Előfeltételek
Ezt az oktatóanyagot az alábbi lehetőségek egyikével követheti:
- Visual Studio 2022 , telepített .NET asztali fejlesztési számítási feladattal. A számítási feladat kiválasztásakor a rendszer automatikusan telepíti a .NET SDK-t.
- A .NET SDK egy tetszőleges kódszerkesztővel, például a Visual Studio Code-tal.
Példaalkalmazás létrehozása
Hozzon létre egy új .NET Core-konzolalkalmazást. Létrehozhat egyet a dotnet new console paranccsal vagy a Visual Studióból.
Nyissa meg a Program.cs fájlt a kódszerkesztőben, és cserélje le a meglévő kódot erre a kódra:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Mezők hozzáadása
Program
Az osztálydefinícióban adja hozzá a következő két mezőt:
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"
};
A HttpClient
lehetővé teszi HTTP-kérések küldését és HTTP-válaszok fogadását. A s_urlList
tartalmazza az alkalmazás által feldolgozni tervezett összes URL-címet.
Alkalmazás belépési pontának frissítése
A konzolalkalmazás fő belépési pontja a Main
metódus. Cserélje le a meglévő metódust a következőre:
static Task Main() => SumPageSizesAsync();
A frissített Main
metódus mostantól Async-főnek minősül, amely lehetővé teszi aszinkron belépési pont használatát a végrehajtható fájlba. Ez a hívásként van kifejezve.SumPageSizesAsync
Az aszinkron sum page sizes metódus létrehozása
Main
A metódus alatt adja hozzá a metódustSumPageSizesAsync
:
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");
}
A while
hurok eltávolítja az egyes iterációk egyik feladatát. Miután minden tevékenység befejeződött, a hurok véget ér. A metódus a példányosítással és egy indítással Stopwatchkezdődik. Ezután tartalmaz egy lekérdezést, amely végrehajtásakor feladatgyűjteményt hoz létre. A következő kód minden hívása ProcessUrlAsync
egy Task<TResult>, egy TResult
egész számot ad vissza:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
A LINQ-val történő halasztott végrehajtás miatt az egyes tevékenységek indítását kell meghívnia Enumerable.ToList .
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
A while
hurok a következő lépéseket hajtja végre a gyűjtemény minden egyes feladatához:
Hívásra vár a
WhenAny
letöltést befejező gyűjtemény első feladatának azonosításához.Task<int> finishedTask = await Task.WhenAny(downloadTasks);
Eltávolítja a feladatot a gyűjteményből.
downloadTasks.Remove(finishedTask);
Vár,
finishedTask
amelyet egy hívás ad vissza a következőnekProcessUrlAsync
: . AfinishedTask
változó egy Task<TResult> egészTResult
szám. A feladat már befejeződött, de várja, hogy lekérje a letöltött webhely hosszát az alábbi példában látható módon. Ha a feladat hibás,await
akkor a tulajdonság olvasásával Task<TResult>.Result ellentétbenAggregateException
a rendszerben tárolt első gyermekkivételt fogja eldobni, ami a következőtAggregateException
eredményezné: .total += await finishedTask;
Folyamatmetódus hozzáadása
Adja hozzá a következő ProcessUrlAsync
metódust a SumPageSizesAsync
metódus alá:
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;
}
Bármely megadott URL-cím esetében a metódus a client
megadott példányt használja a válasz byte[]
lekéréséhez. A rendszer azt követően adja vissza a hosszt, hogy az URL-cím és a hossz meg van írva a konzolra.
Futtassa többször a programot annak ellenőrzéséhez, hogy a letöltött hosszok nem mindig ugyanabban a sorrendben jelennek-e meg.
Figyelemfelhívás
A példában ismertetett hurokban olyan problémákat is megoldhat WhenAny
, amelyek kis számú feladatot érintenek. Más megközelítések azonban hatékonyabbak, ha sok feladatot kell feldolgoznia. További információkért és példákért lásd: Feladatok feldolgozása befejezettként.
Teljes példa
A következő kód a példa Program.cs fájljának teljes szövege.
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
Lásd még
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: