Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A használatával Task.WhenAnyegyszerre több tevékenységet is elindíthat, és egyenként feldolgozhatja őket a befejezésükkor, ahelyett, hogy a kezdési sorrendben dolgozzák fel őket.
Az alábbi példa egy lekérdezés használatával hoz létre feladatgyűjteményt. Minden feladat letölti egy adott webhely tartalmát. Egy while ciklus minden iterációjában egy várt hívás a WhenAny függvényre visszaadja a feladatot a feladatok gyűjteményéből, amelyik elsőként fejezi be a letöltését. A feladat eltávolításra kerül a gyűjteményből, és feldolgozásra kerül. A ciklus 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 a .NET asztali fejlesztési munkaállomással telepítve. A számítási feladat kiválasztásakor a rendszer automatikusan telepíti a .NET SDK-t.
- A .NET SDK valamilyen kódszerkesztővel a választásod szerint, 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 új konzolparancsával 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 a következő 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"
};
Ez HttpClient lehetővé teszi HTTP-kérések küldését és HTTP-válaszok fogadását. Az s_urlList alkalmazás által feldolgozni kívánt összes URL-címet tartalmazza.
Alkalmazás belépési pont 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 Aszinkron főnek minősül, amely lehetővé teszi aszinkron belépési pontot a végrehajtható fájlba. Ez hívásként van kifejezve SumPageSizesAsync.
Az aszinkron oldal méretek összeadásának metódusát létrehozni
A Main metódus alatt adja hozzá a SumPageSizesAsync metódust.
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 ciklus eltávolítja az egyes iterációk egyik feladatát. Miután minden feladat befejeződött, a hurok befejeződik. A metódus a Stopwatch példányosításával és indításával kezdődik. Ezután tartalmaz egy lekérdezést, amely végrehajtásakor feladatgyűjteményt hoz létre. A következő kódban minden ProcessUrlAsync hívás egy Task<TResult> ad vissza, ahol TResult egy egész szám.
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ára kell hí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:
WhenAny-ra várakozik, hogy azonosítsa a gyűjteményben azt az első feladatot, amely befejezte a letöltést.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Azt a feladatot eltávolítja a gyűjteményből.
downloadTasks.Remove(finishedTask);Várható, hogy a(z)
finishedTaskvisszatér egyProcessUrlAsynchívás eredményeként. AfinishedTaskváltozó egy egészTResultszám, ahol a Task<TResult>. A feladat már készen van, de vár arra, hogy kiderüljön a letöltött weboldal mérete, ahogy az alábbi példa is mutatja. Ha a feladat hibás,awaitaz első gyermekkivételt dobja, amit aAggregateExceptiontárol, ellentétben a Task<TResult>.Result tulajdonság olvasásával, amely aAggregateExceptionkivételt dobná.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 az URL-cím és a hossz konzolra írása után adja vissza a hosszt.
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.
Caution
A példában ismertetett ciklusban olyan problémák megoldására használhatja WhenAny , amelyek kis számú feladatot foglalnak magukban. 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 tekintse meg a Feladatok feldolgozása, ahogy befejeződnek.
"Egyszerűsítse a megközelítést a Task.WhenEach segítségével"
A while metódusban SumPageSizesAsync implementált hurok egyszerűsíthető a .NET 9-ben bevezetett új Task.WhenEach metódus használatával, ha azt a await foreach hurokban hívja meg.
Cseréljük le a korábban megvalósított while hurkot:
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
az egyszerűsített await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Az új megközelítés lehetővé teszi, hogy ne kelljen többé ismételten Task.WhenAny-t hívni egy feladat manuális elvégzésére, és eltávolítani azt, amelyik befejeződött, mert Task.WhenEach a feladatokon a befejezésük sorrendjében iterál.
Teljes példa
A következő kód a példához tartozó Program.cs fájl 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