Zaman uyumsuz görevleri tamamlandıklarında işleme (C#)

kullanarak Task.WhenAny, birden çok görevi aynı anda başlatabilir ve başlatıldıkları sırada işlemek yerine tamamlandıkları sırada tek tek işleyebilirsiniz.

Aşağıdaki örnekte, bir görev koleksiyonu oluşturmak için bir sorgu kullanılır. Her görev, belirtilen web sitesinin içeriğini indirir. Bir while döngüsünün her yinelemesinde, ilk olarak indirme işlemini tamamlayan görev koleksiyonundaki görevi döndürmek için WhenAny beklenen bir çağrı. Bu görev koleksiyondan kaldırılır ve işlenir. Döngü, koleksiyon başka görev içermeyene kadar yineler.

Ön koşullar

Aşağıdaki seçeneklerden birini kullanarak bu öğreticiyi izleyebilirsiniz:

Örnek uygulama oluşturma

Yeni bir .NET Core konsol uygulaması oluşturun. Dotnet yeni konsol komutunu kullanarak veya Visual Studio'dan bir konsol oluşturabilirsiniz.

Kod düzenleyicinizde Program.cs dosyasını açın ve mevcut 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 tutar.

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 bir Zaman Uyumsuz ana değer olarak kabul edilir. çağrısı olarak SumPageSizesAsyncifade 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 bir Task<TResult>döndürür; burada TResult bir tamsayıdı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ısında Enumerable.ToList bulunursunuz.

List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

döngü, while koleksiyondaki her görev için aşağıdaki adımları gerçekleştirir:

  1. Koleksiyonun indirme işlemini tamamlayan ilk görevini tanımlamak için çağrısı WhenAny bekler.

    Task<int> finishedTask = await Task.WhenAny(downloadTasks);
    
  2. Bu görevi koleksiyondan kaldırır.

    downloadTasks.Remove(finishedTask);
    
  3. finishedTaskçağrısı tarafından ProcessUrlAsyncdöndürülen öğesini bekler. finishedTask değişkeni, bir Task<TResult> tamsayı olan bir yerdirTResult. Görev zaten tamamlandı, ancak aşağıdaki örnekte gösterildiği gibi indirilen web sitesinin uzunluğunu almasını bekliyorsunuz. Görev hatalıysa, await özelliğini okumaktan Task<TResult>.Result farklı olarak içinde AggregateExceptiondepolanan ilk alt özel durumu oluşturur ve bu da değerini oluştururAggregateException.

    total += await finishedTask;
    

İşlem yöntemi ekleme

Aşağıdaki ProcessUrlAsync yöntemi yönteminin SumPageSizesAsync altına 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 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.

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

Ayrıca bkz.