Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dengan menggunakan Task.WhenAny, Anda dapat memulai beberapa tugas secara bersamaan dan memprosesnya satu per satu saat tugas selesai daripada memprosesnya dalam urutan dimulai.
Contoh berikut menggunakan kueri untuk membuat kumpulan tugas. Setiap tugas mengunduh konten situs web tertentu. Pada setiap iterasi dari perulangan while loop, panggilan yang ditunggu untuk WhenAny mengembalikan tugas dalam kumpulan tugas yang menyelesaikan unduhannya terlebih dahulu. Tugas tersebut dihapus dari koleksi dan diproses. Perulangan diulang hingga koleksi tidak berisi lebih banyak tugas.
Prasyarat
Anda dapat mengikuti tutorial ini dengan menggunakan salah satu opsi berikut:
- Visual Studio 2022 dengan workload pengembangan aplikasi desktop .NET yang sudah terpasang. .NET SDK secara otomatis diinstal saat Anda memilih beban kerja ini.
- .NET SDK dengan editor kode pilihan Anda, seperti Visual Studio Code.
Membuat aplikasi contoh
Buat aplikasi konsol .NET Core baru. Anda dapat membuatnya dengan menggunakan perintah konsol baru dotnet atau dari Visual Studio.
Buka file Program.cs di editor kode Anda, dan ganti kode yang ada dengan kode ini:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Menambahkan kolom
Program Dalam definisi kelas, tambahkan dua bidang berikut:
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/dotnet/maui"
};
HttpClient mengungkapkan fitur untuk mengirim permintaan HTTP dan menerima respons HTTP. Semua URL s_urlList yang akan diproses oleh aplikasi ditampung.
Memperbarui titik entri aplikasi
Titik masuk utama ke dalam aplikasi konsol adalah metode .Main Ganti metode yang ada dengan yang berikut ini:
static Task Main() => SumPageSizesAsync();
Metode yang diperbarui Main sekarang dianggap sebagai Async main, yang memungkinkan titik masuk asinkron ke dalam executable. Ini dinyatakan sebagai panggilan ke SumPageSizesAsync.
Membuat metode penghitungan ukuran halaman secara asinkron
Di bawah metode Main, tambahkan metode SumPageSizesAsync.
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");
}
Perulangan while menghapus salah satu tugas di setiap perulangan. Setelah setiap tugas selesai, perulangan berakhir. Metode ini dimulai dengan membuat instans dan memulai Stopwatch. Kemudian menyertakan kueri yang, saat dijalankan, membuat kumpulan tugas. Setiap panggilan ke ProcessUrlAsync dalam kode berikut mengembalikan Task<TResult>, di mana TResult adalah bilangan bulat:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Karena eksekusi yang ditangguhkan dengan LINQ, Anda memanggil Enumerable.ToList untuk memulai setiap tugas.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Perulangan while menjalankan langkah-langkah berikut pada setiap tugas dalam koleksi tersebut:
Menunggu panggilan ke
WhenAnyuntuk mengidentifikasi tugas pertama dalam koleksi yang sudah menyelesaikan proses pengunduhannya.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Menghapus tugas tersebut dari koleksi data.
downloadTasks.Remove(finishedTask);Menunggu
finishedTask, yang dikembalikan melalui panggilan keProcessUrlAsync. VariabelfinishedTaskadalah Task<TResult> di manaTResultadalah bilangan bulat. Tugas sudah selesai, tetapi Anda menunggu hingga mendapatkan ukuran situs web yang terunduh, seperti yang ditunjukkan contoh berikut. Jika tugas gagal,awaitakan melemparkan pengecualian anak pertama yang disimpan diAggregateException, tidak seperti membaca properti Task<TResult>.Result, yang akan melemparkanAggregateException.total += await finishedTask;
Menambahkan metode proses
Tambahkan metode berikut ProcessUrlAsync di bawah SumPageSizesAsync metode :
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;
}
Untuk URL apapun, metode akan menggunakan client instans yang disediakan untuk mendapatkan respons sebagai byte[]. Panjang akan dikembalikan setelah URL dan panjang tersebut dicatat di konsol.
Jalankan program beberapa kali untuk memverifikasi bahwa panjang yang diunduh tidak selalu muncul dalam urutan yang sama.
Perhatian
Anda dapat menggunakan WhenAny dalam perulangan, seperti yang dijelaskan dalam contoh, untuk menyelesaikan masalah yang melibatkan sejumlah kecil tugas. Namun, pendekatan lain lebih efisien jika Anda memiliki sejumlah besar tugas untuk diproses. Untuk informasi dan contoh selengkapnya, lihat Memproses tugas saat selesai.
Menyederhanakan pendekatan menggunakan Task.WhenEach
while loop yang diimplementasikan dalam metode SumPageSizesAsync dapat disederhanakan dengan menggunakan metode baru Task.WhenEach yang diperkenalkan di .NET 9, dengan memanggilnya dalam await foreach loop.
Ganti perulangan yang telah diimplementasikan while sebelumnya.
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
dengan yang sederhana await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Pendekatan baru ini memungkinkan untuk tidak lagi berulang kali memanggil Task.WhenAny untuk memanggil tugas secara manual dan menghapus tugas yang selesai, karena Task.WhenEach berulang melalui tugas dalam urutan penyelesaiannya.
Contoh lengkap
Kode berikut adalah teks lengkap file Program.cs untuk contoh.
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/dotnet/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/dotnet/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