Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
kullanarak Task.WhenAny, birden çok görevi aynı anda başlatabilir ve tamamlandıkları sırada işlemek yerine tek tek işleyebilirsiniz.
Aşağıdaki örnek, bir görev koleksiyonu oluşturmak için bir sorgu kullanır. Her görev, belirtilen bir web sitesinin içeriğini indirir. Bir while döngüsünün her yinelemesinde, beklenen çağrısı WhenAny , önce indirme işlemini tamamlayan görev koleksiyonundaki görevi döndürür. Bu görev koleksiyondan kaldırılır ve işlenir. Döngü, koleksiyon başka görev içermeyene kadar yineler.
Önkoşullar
Aşağıdaki seçeneklerden birini kullanarak bu öğreticiyi izleyebilirsiniz:
- Visual Studio 2022 üzerinde kurulu .NET masaüstü geliştirme iş yükü. Bu iş yükünü seçtiğinizde .NET SDK'sı otomatik olarak yüklenir.
- Visual Studio Code gibi, seçtiğiniz bir kod düzenleyicisine sahip .NET SDK.
Örnek uygulama oluşturma
Yeni bir .NET Core konsol uygulaması oluşturun. Dotnet yeni konsol komutunu kullanarak veya Visual Studio'dan oluşturabilirsiniz.
kod düzenleyicinizde Program.cs dosyasını açın ve var olan kodu şu kodla değiştirin:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Alan ekleme
Sınıf tanımına Program aşağıdaki iki alanı ekleyin:
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"
};
, HttpClient HTTP istekleri gönderme ve HTTP yanıtları alma özelliğini kullanıma sunar. , s_urlList uygulamanın işlemeyi planladığı tüm URL'leri barındırır.
Uygulama giriş noktasını güncelleştirme
Konsol uygulamasının ana giriş noktası yöntemidir Main . Mevcut yöntemi aşağıdakilerle değiştirin:
static Task Main() => SumPageSizesAsync();
Güncelleştirilmiş Main yöntem artık yürütülebilir dosyaya zaman uyumsuz bir giriş noktası sağlayan Async ana değeri olarak kabul edilir. çağrısı SumPageSizesAsyncolarak ifade edilir.
Zaman uyumsuz toplam sayfa boyutları yöntemini oluşturma
yönteminin Main altına yöntemini ekleyin 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");
}
döngü, while her yinelemedeki görevlerden birini kaldırır. Her görev tamamlandıktan sonra döngü sona erer. yöntemi, örneği oluşturarak ve başlatarak Stopwatchbaşlar. Ardından yürütülürken bir görev koleksiyonu oluşturan bir sorgu içerir. Aşağıdaki koddaki her çağrısıProcessUrlAsync, tamsayı olan TResult bir Task<TResult>döndürür:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
LINQ ile ertelenen yürütme nedeniyle, her görevi başlatmak için çağırırsınız Enumerable.ToList .
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Döngü, while koleksiyondaki her görev için aşağıdaki adımları gerçekleştirir:
Koleksiyonun indirme işlemini tamamlayan ilk görevini tanımlamak için bir çağrı
WhenAnybekler.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Bu görevi koleksiyondan kaldırır.
downloadTasks.Remove(finishedTask);awaits
finishedTask, çağrısıProcessUrlAsynctarafından döndürülür.finishedTaskdeğişkeni, Task<TResult> bir tamsayı olan bir yerdirTResult. Görev zaten tamamlandı, ancak aşağıdaki örnekte gösterildiği gibi indirilen web sitesinin uzunluğunu almayı bekliyorsunuz. Görev hatalıysa,awaitözelliğini okumaktan Task<TResult>.Result farklı olarak içindeAggregateExceptiondepolanan ilk alt özel durumu oluşturur ve bu da değerini oluştururAggregateException.total += await finishedTask;
İşlem yöntemi ekleme
Yönteminin altına SumPageSizesAsync aşağıdaki ProcessUrlAsync yöntemi ekleyin:
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;
}
Herhangi bir URL için yöntemi, yanıtı olarak byte[]almak için sağlanan örneği kullanırclient. Url ve uzunluk konsola yazıldıktan sonra uzunluk döndürülür.
İndirilen uzunlukların her zaman aynı sırada görünmediğini doğrulamak için programı birkaç kez çalıştırın.
Dikkat
Az sayıda görev içeren sorunları çözmek için örnekte açıklandığı gibi bir döngüde kullanabilirsiniz WhenAny . Ancak, işlenmek üzere çok sayıda göreviniz varsa diğer yaklaşımlar daha verimlidir. Daha fazla bilgi ve örnek için bkz . Görevleri tamamlandıklarında işleme.
Kullanarak yaklaşımı basitleştirme Task.WhenEach
while yönteminde SumPageSizesAsync uygulanan döngü, .NET 9'da tanıtılan yeni Task.WhenEach yöntem kullanılarak döngü içinde await foreach çağrılarak basitleştirilebilir.
Daha önce uygulanan while döngünün yerine:
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
ile basitleştirilmiş await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Bu yeni yaklaşım artık bir görevi el ile çağırmak ve tamamlayanı kaldırmak için art arda çağrı Task.WhenAny yapmamaya olanak tanır, çünkü Task.WhenEach görev tamamlanmasının sırasına göre yinelenir.
Tam örnek
Aşağıdaki kod, örnek için Program.cs dosyasının tam metnidir.
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