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 időhurok minden iterációjában egy várt hívás WhenAny adja vissza a feladatot a letöltést először befejező tevékenységek gyűjteményében. A rendszer eltávolítja a feladatot a gyűjteményből, és feldolgozzák. 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 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 ú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 a hívásként SumPageSizesAsyncvan kifejezve.
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 ciklus 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 kezdődik, és elindít egy Stopwatch. 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>, ahol TResult egy egész szám szerepel:
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:
Hívásra
WhenAnyvár a letöltést befejező gyűjtemény első feladatának azonosítására.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Eltávolítja a feladatot a gyűjteményből.
downloadTasks.Remove(finishedTask);finishedTaskVár, amelyet egy hívás adProcessUrlAsyncvissza. AfinishedTaskváltozó egy Task<TResult> egészTResultszám. A feladat már befejeződött, de várja, hogy lekérje a letöltött webhely hosszát, ahogy az alábbi példa is mutatja. Ha a feladat hibás,awaitakkor a tulajdonság olvasásával Task<TResult>.Result ellentétben az első gyermekkivételt a program a következőbenAggregateExceptiontárolja:AggregateExceptiontotal += 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 befejezettként című témakört.
A módszer egyszerűsítése Task.WhenEach
A while metódusban SumPageSizesAsync implementált hurok a .NET 9-ben bevezetett új Task.WhenEach metódussal egyszerűsíthető úgy, hogy hurkot await foreach hív meg.
Cserélje le a korábban implementált hurkot while :
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;
}
Ez az új megközelítés lehetővé teszi, hogy többé ne hívjon Task.WhenAny meg manuálisan egy tevékenységet, és távolítsa el azt, amelyik befejeződött, mert Task.WhenEach a feladat végigfut a befejezés sorrendjében.
Példa kitöltése
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