Megosztás a következőn keresztül:


Aszinkron feladatok feldolgozása a befejezésük során (C#)

A használatával Task.WhenAnyegyszerre több tevékenységet is elindíthat, és egyenként feldolgozhatja őket, miközben befejeződnek, ahelyett, hogy a kezdési sorrendben dolgoznák fel őket.

Az alábbi példa egy lekérdezéssel 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 feladatgyűjteményben, amely először befejezi a letöltést. A rendszer eltávolítja a feladatot a gyűjteményből, és feldolgozzák. A hurok 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 , telepített .NET asztali fejlesztési számítási feladattal. 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 new console paranccsal 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 erre a 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"
};

A HttpClient lehetővé teszi HTTP-kérések küldését és HTTP-válaszok fogadását. A s_urlList tartalmazza az alkalmazás által feldolgozni tervezett összes URL-címet.

Alkalmazás belépési pontának 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 Async-főnek minősül, amely lehetővé teszi aszinkron belépési pont használatát a végrehajtható fájlba. Ez a hívásként van kifejezve.SumPageSizesAsync

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 hurok 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 és egy indítással Stopwatchkezdődik. 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>, egy TResult egész számot ad vissza:

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át kell meghí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:

  1. Hívásra vár a WhenAny letöltést befejező gyűjtemény első feladatának azonosításához.

    Task<int> finishedTask = await Task.WhenAny(downloadTasks);
    
  2. Eltávolítja a feladatot a gyűjteményből.

    downloadTasks.Remove(finishedTask);
    
  3. Vár, finishedTaskamelyet egy hívás ad vissza a következőnek ProcessUrlAsync: . A finishedTask változó egy Task<TResult> egész TResult szám. A feladat már befejeződött, de várja, hogy lekérje a letöltött webhely hosszát az alábbi példában látható módon. Ha a feladat hibás, await akkor a tulajdonság olvasásával Task<TResult>.Result ellentétben AggregateExceptiona rendszerben tárolt első gyermekkivételt fogja eldobni, ami a következőt AggregateExceptioneredményezné: .

    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 azt követően adja vissza a hosszt, hogy az URL-cím és a hossz meg van írva a konzolra.

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.

Figyelemfelhívás

A példában ismertetett hurokban olyan problémákat is megoldhat WhenAny , amelyek kis számú feladatot érintenek. 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 lásd: Feladatok feldolgozása befejezettként.

Teljes példa

A következő kód a példa Program.cs fájljának 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

Lásd még