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


Aszinkron műveletek létrehozása C++ nyelven UWP-alkalmazásokhoz

Ez a dokumentum néhány fontos szempontot ismertet, amelyet szem előtt kell tartani, amikor a feladatosztály használatával Windows ThreadPool-alapú aszinkron műveleteket hoz létre egy univerzális Windows-futtatókörnyezeti (UWP) alkalmazásban.

Az aszinkron programozás a Windows futtatókörnyezeti alkalmazásmodell kulcsfontosságú összetevője, mivel lehetővé teszi, hogy az alkalmazások továbbra is reagáljanak a felhasználói bemenetre. A felhasználói felületi szál blokkolása nélkül is elindíthat egy hosszú ideig futó feladatot, és később is megkaphatja a feladat eredményeit. Feladatokat is megszakíthatsz, és értesítéseket kaphatsz az előrehaladásról, miközben a feladatok a háttérben futnak. A C++ dokumentum aszinkron programozása áttekintést nyújt a Visual C++-ban elérhető aszinkron mintáról UWP-alkalmazások létrehozásához. Ez a dokumentum bemutatja, hogyan lehet egyszerre használni és létrehozni az aszinkron Windows-futtatókörnyezeti műveletek láncait. Ez a szakasz azt ismerteti, hogyan használható a ppltasks.h típusok egy másik Windows-futtatókörnyezeti összetevő által felhasználható aszinkron műveletek előállítására, valamint az aszinkron munka végrehajtásának szabályozására. Érdemes lehet elolvasni az Async programozási mintákat és tippeket a Hilo-ban (C++ és XAML használatával készült Windows Áruházbeli alkalmazások), hogy megtudjuk, hogyan használtuk a feladatosztályt az aszinkron műveletek végrehajtására a Hilo-ban, egy Windows Runtime-alkalmazásban C++ és XAML használatával.

Megjegyzés:

A párhuzamos minták kódtára (PPL) és aszinkron ügynökök kódtára egy UWP-alkalmazásban használható. A Feladatütemezőt és a Resource Managert azonban nem használhatja. Ez a dokumentum azokat a további funkciókat ismerteti, amelyeket a PPL biztosít, amelyek csak egy UWP-alkalmazás számára érhetők el, asztali alkalmazások számára nem.

Kulcsfontosságú pontok

  • Egyidejűség::create_async olyan aszinkron műveletek létrehozásához, amelyeket más összetevők is használhatnak (amelyek a C++-tól eltérő nyelveken írhatók).

  • Az concurrency::progress_reporter használatával előrehaladási jelentéseket küldhet az aszinkron műveleteket meghívó összetevőknek.

  • A lemondási tokenek használatával lehetővé teheti a belső aszinkron műveletek megszakítását.

  • A függvény viselkedése a create_async neki átadott munkafüggvény visszatérési típusától függ. Az a munkafüggvény, amely feladatot (akár task<T> vagy task<void>) ad vissza, szinkron módon fut abban a környezetben, amelyből a create_async-t hívták meg. Tetszőleges környezetben futtatható egy munkafüggvény, amely visszaadja a T vagy a void értéket.

  • Az egyidejűség::task::then metódus használatával létrehozhat egy egymást követő feladatláncot. Az UWP-alkalmazásokban a tevékenység folytatásának alapértelmezett környezete attól függ, hogy hogyan lett létrehozva. Ha a tevékenység úgy jött létre, hogy aszinkron műveletet ad át a tevékenységkonstruktornak, vagy egy aszinkron műveletet visszaadó lambda-kifejezés átadásával jött létre, akkor a tevékenység minden folytatásának alapértelmezett környezete az aktuális környezet. Ha a tevékenység nem aszinkron műveletből épül fel, akkor a rendszer alapértelmezés szerint egy tetszőleges környezetet használ a tevékenység folytatásához. Az alapértelmezett környezetet a concurrency::task_continuation_context osztállyal felülbírálhatja.

Ebben a dokumentumban

Aszinkron műveletek létrehozása

A párhuzamos minták tárában (PPL) a tevékenység- és folytatási modell használatával definiálhatja a háttérfeladatokat, valamint az előző tevékenység befejezésekor futtatott további tevékenységeket. Ezt a funkciót az egyidejűség::feladatosztály biztosítja. A modellről és az osztályról további információt a taskfeladat-párhuzamosság című témakörben talál.

A Windows Futtatókörnyezet egy olyan programozási felület, amellyel olyan UWP-alkalmazásokat hozhat létre, amelyek csak speciális operációsrendszer-környezetben futnak. Az ilyen alkalmazások engedélyezett függvényeket, adattípusokat és eszközöket használnak, és a Microsoft Store-ból vannak elosztva. A Windows futtatókörnyezetet az Application Binary Interface (ABI) képviseli. Az ABI egy mögöttes bináris szerződés, amely elérhetővé teszi a Windows Runtime API-kat olyan programozási nyelvek számára, mint a Visual C++.

A Windows Futtatókörnyezet használatával a különböző programozási nyelvek legjobb funkcióit használhatja, és egyetlen alkalmazásba kombinálhatja őket. Létrehozhatja például a felhasználói felületet a JavaScriptben, és végrehajthatja a számításigényes alkalmazáslogikát egy C++ összetevőben. Ezeknek a számításigényes műveleteknek a háttérben való végrehajtásának képessége kulcsfontosságú tényező a felhasználói felület válaszkészségében. Mivel az task osztály a C++-ra jellemző, windowsos futtatókörnyezeti felülettel kell kommunikálnia az aszinkron műveleteket más összetevőkkel (amelyek a C++-tól eltérő nyelveken írhatók). A Windows futtatókörnyezet négy felületet biztosít, amelyekkel aszinkron műveleteket jelölhet:

Windows::Foundation::IAsyncAction
Aszinkron műveletet jelöl.

Windows::Foundation::IAsyncActionWithProgress<TProgress>
A folyamat előrehaladását jelző aszinkron műveletet jelöl.

Windows::Foundation::IAsyncOperation<TResult>
Egy aszinkron műveletet jelöl, amely egy eredményt ad vissza.

Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Egy aszinkron műveletet jelöl, amely egy eredményt ad vissza, és jelzi az előrehaladást.

A művelet fogalma azt jelenti, hogy az aszinkron tevékenység nem hoz létre értéket (gondoljunk egy visszaadott függvényre void). A művelet fogalma azt jelenti, hogy az aszinkron tevékenység létrehoz egy értéket. A folyamat fogalma azt jelenti, hogy a tevékenység jelentést küldhet a hívónak a folyamat előrehaladási üzeneteiről. A JavaScript, a .NET-keretrendszer és a Visual C++ mind saját módot kínál ezeknek a felületeknek a példányainak létrehozására az ABI-határokon keresztül történő használatra. A Visual C++ esetében a PPL biztosítja a concurrency::create_async függvényt. Ez a függvény létrehoz egy Windows Futtatókörnyezet aszinkron műveletet vagy műveletet, amely egy tevékenység befejezését jelöli. A create_async függvény egy munkafüggvényt (általában lambda kifejezést) vesz igénybe, belsőleg létrehoz egy task objektumot, és a feladatot a négy aszinkron Windows-futtatókörnyezeti felület egyikén burkolja.

Megjegyzés:

Csak akkor használja create_async , ha olyan funkciókat kell létrehoznia, amelyek egy másik nyelvről vagy egy másik Windows-futtatókörnyezeti összetevőből érhetők el. Közvetlenül akkor használja az task osztályt, ha tudja, hogy a műveletet ugyanabban az összetevőben található C++ kód hozza létre és használja fel.

A visszatérési create_async típust az argumentumok típusa határozza meg. Ha például a munkafüggvény nem ad vissza értéket, és nem jelenti az előrehaladást, create_async akkor a függvény eredménye IAsyncAction. Ha a munkafüggvény nem ad vissza értéket, és az előrehaladást is jelenti, akkor a create_asyncIAsyncActionWithProgress-t ad vissza. A haladás jelentéséhez adjon meg egy concurrency::progress_reporter objektumot a munkafüggvény paramétereként. Az előrehaladás jelentésének lehetősége lehetővé teszi, hogy bejelentse, milyen mennyiségű munkát végeztek el, és milyen összeg marad még (például százalékban). Lehetővé teszi az eredmények jelentését is, amint azok elérhetővé válnak.

A IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, és IAsyncActionOperationWithProgress<TProgress, TProgress> interfészek mindegyike egy metódust Cancel biztosít, amely lehetővé teszi az aszinkron művelet megszakítását. Az task osztály a megszakítási tokenekkel működik. Ha lemondási jogkivonatot használ a munka megszakításához, a futtatókörnyezet nem indít el új munkát, amely az adott jogkivonatra előfizet. A már aktív munka figyelheti a műveletmegsemmisítő tokent, és leállíthatja a munkát, amikor csak szükséges. Ezt a mechanizmust részletesebben a PPL-beli Lemondás című dokumentumban ismertetjük. A feladattörlést kétféleképpen csatlakoztathatja a Windows futtatókörnyezeti Cancel metódusokkal. Először is, megadhatja azt a munkafüggvényt, amelyet átad create_async-nek, hogy vegyen egy concurrency::cancellation_token objektumot. A Cancel metódus meghívásakor megszünteti ezt a lemondási tokent, és a normál lemondási szabályok vonatkoznak a task hívást támogató mögöttes create_async objektumra. Ha nem ad meg objektumot cancellation_token , a mögöttes task objektum implicit módon definiál egyet. Definiáljon egy objektumot cancellation_token , ha együttműködési választ kell adnia a munkahelyi függvény lemondására. A Példa szakasz: A végrehajtás szabályozása egy C++ és XAML rendszerű Windows-futtatókörnyezeti alkalmazásban egy egyéni Windows-futtatókörnyezeti C++ összetevőt használó univerzális Windows-platform (UWP) alkalmazásban történő törlés végrehajtására.

Figyelmeztetés

A tevékenységek folytatási láncában mindig törölje az állapotot, majd hívja meg concurrency::cancel_current_task, amikor a törlési token érvénybe lép. Ha a cancel_current_task helyett korán tér vissza, a művelet a törölt állapot helyett a befejezett állapotra vált.

Az alábbi táblázat összefoglalja azokat a kombinációkat, amelyekkel aszinkron műveleteket definiálhat az alkalmazásban.

A Windows futtatókörnyezeti felület létrehozása Ezt a típust adja vissza a create_async Adja át ezeket a paramétertípusokat a munkafüggvénynek implicit törlési token használatához Adja át ezeket a paramétertípusokat a munkafüggvénynek, hogy egy explicit megszakítási tokent használjon.
IAsyncAction void vagy task<void> (nincs) (cancellation_token)
IAsyncActionWithProgress<TProgress> void vagy task<void> (progress_reporter) (progress_reporter, cancellation_token)
IAsyncOperation<TResult> T vagy task<T> (nincs) (cancellation_token)
IAsyncActionOperationWithProgress<TProgress, TProgress> T vagy task<T> (progress_reporter) (progress_reporter, cancellation_token)

Visszaadhat egy értéket vagy egy task objektumot a függvénynek create_async átadott munkafüggvényből. Ezek a változatok eltérő viselkedést eredményeznek. Amikor visszaad egy értéket, a munkafüggvény úgy csomagolódik be egy task-ba, hogy futtatható legyen a háttérszálon. Emellett az alapul szolgáló task egy implicit törlési jogkivonatot is használ. Ezzel szemben, ha egy objektumot task ad vissza, a munkafüggvény szinkron módon fut. Ezért ha egy task objektumot ad vissza, győződjön meg arról, hogy a munkafüggvény minden hosszadalmas művelete feladatként is fut, hogy az alkalmazás válaszképes maradjon. Emellett az alapul szolgáló task nem használ implicit lemondási tokent. Ezért meg kell határoznia a munkafüggvényt, hogy elfogadjon egy cancellation_token objektumot, ha támogatást igényel a törléshez, amikor egy task objektumot ad vissza a create_async-ból.

Az alábbi példa egy másik Windows-futtatókörnyezeti összetevő által felhasználható objektum létrehozásának IAsyncAction különböző módjait mutatja be.

// Creates an IAsyncAction object and uses an implicit cancellation token.
auto op1 = create_async([]
{
    // Define work here.
});

// Creates an IAsyncAction object and uses no cancellation token.
auto op2 = create_async([]
{
    return create_task([]
    {
        // Define work here.
    });
});

// Creates an IAsyncAction object and uses an explicit cancellation token.
auto op3 = create_async([](cancellation_token ct)
{
    // Define work here.
});

// Creates an IAsyncAction object that runs another task and also uses an explicit cancellation token.
auto op4 = create_async([](cancellation_token ct)
{
    return create_task([ct]()
    {
        // Define work here.
    });
});

Példa: C++ Windows futtatókörnyezeti összetevő létrehozása és felhasználása C-ből#

Fontolja meg egy olyan alkalmazást, amely XAML és C# használatával határozza meg a felhasználói felületet és egy C++ Windows-futtatókörnyezeti összetevőt a nagy számítási igényű műveletek végrehajtásához. Ebben a példában a C++ összetevő kiszámítja, hogy egy adott tartomány mely számai prímek. A négy Windows Runtime aszinkron feladat interfész közötti különbségek szemléltetéséhez kezdje el azzal, hogy létrehoz egy Visual Studio üres megoldást és elnevezi Primes. Ezután adjon hozzá egy Windows Runtime Component-projektet a megoldáshoz, és nevezd el.PrimesLibrary Adja hozzá a következő kódot a létrehozott C++ fejlécfájlhoz (ez a példa átnevezi a Class1.h osztályt Primes.h-ra). Minden public metódus a négy aszinkron interfész egyikét határozza meg. Az értéket visszaadó metódusok egy Windows::Foundation::Collections::IVector<int objektumot> adnak vissza. Az előrehaladást jelentéskészítő metódusok olyan értékeket hoznak létre double , amelyek meghatározzák a befejezett munka százalékos arányát.

#pragma once

namespace PrimesLibrary
{
    public ref class Primes sealed
    {
    public:
        Primes();

        // Computes the numbers that are prime in the provided range and stores them in an internal variable.
        Windows::Foundation::IAsyncAction^ ComputePrimesAsync(int first, int last);

        // Computes the numbers that are prime in the provided range and stores them in an internal variable.
        // This version also reports progress messages.
        Windows::Foundation::IAsyncActionWithProgress<double>^ ComputePrimesWithProgressAsync(int first, int last);

        // Gets the numbers that are prime in the provided range.
        Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<int>^>^ GetPrimesAsync(int first, int last);

        // Gets the numbers that are prime in the provided range. This version also reports progress messages.
        Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ GetPrimesWithProgressAsync(int first, int last);
    };
}

Megjegyzés:

Konvenció szerint a Windows-futtatókörnyezet aszinkron metódusnevei általában "Async"-sel végződnek.

Adja hozzá a következő kódot a létrehozott C++ forrásfájlhoz (ez a példa átnevezi Class1.cpp Primes.cpp). A is_prime függvény meghatározza, hogy a bemenete prím. A többi metódus implementálja az osztályt Primes . Minden hívás create_async olyan aláírást használ, amely kompatibilis azzal a metódussal, amelyből származik. Például, mivel Primes::ComputePrimesAsync visszatérít egy IAsyncAction-et, az create_async-nek biztosított munkafüggvény nem ad vissza értéket, és nem fogad el egy progress_reporter objektumot paraméterként.

// PrimesLibrary.cpp
#include "pch.h"
#include "Primes.h"
#include <atomic>
#include <collection.h>
#include <ppltasks.h>
#include <concurrent_vector.h>

using namespace concurrency;
using namespace std;

using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;

using namespace PrimesLibrary;

Primes::Primes()
{
}

// Determines whether the input value is prime. 
bool is_prime(int n)
{
    if (n < 2)
    {
        return false;
    }
    for (int i = 2; i < n; ++i)
    {
        if ((n % i) == 0)
        {
            return false;
        }
    }
    return true;
}

// Adds the numbers that are prime in the provided range  
// to the primes global variable.
IAsyncAction^ Primes::ComputePrimesAsync(int first, int last)
{
    return create_async([this, first, last]
    {
        // Ensure that the input values are in range. 
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        parallel_for(first, last + 1, [this](int n)
        {
            if (is_prime(n))
            {
                // Perhaps store the value somewhere...
            }
        });
    });
}

IAsyncActionWithProgress<double>^ Primes::ComputePrimesWithProgressAsync(int first, int last)
{
    return create_async([first, last](progress_reporter<double> reporter)
    {
        // Ensure that the input values are in range.
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel. 
        atomic<long> operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;
        parallel_for(first, last + 1, [&operation, range, &lastPercent, reporter](int n)
        {
            // Report progress message.
            double progress = 100.0 * (++operation) / range;
            if (progress >= lastPercent)
            {
                reporter.report(progress);
                lastPercent += 1.0;
            }

            if (is_prime(n))
            {
                // Perhaps store the value somewhere...
            }
        });
        reporter.report(100.0);
    });
}

IAsyncOperation<IVector<int>^>^ Primes::GetPrimesAsync(int first, int last)
{
    return create_async([this, first, last]() -> IVector<int>^
    {
        // Ensure that the input values are in range. 
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        concurrent_vector<int> primes;
        parallel_for(first, last + 1, [this, &primes](int n)
        {
            // If the value is prime, add it to the global vector.
            if (is_prime(n))
            {
                primes.push_back(n);
            }
        });
        // Sort the results.
        sort(begin(primes), end(primes), less<int>());

        // Copy the results to an IVector object. The IVector 
        // interface makes collections of data available to other 
        // Windows Runtime components.
        auto results = ref new Vector<int>();
        for (int prime : primes)
        {
            results->Append(prime);
        }
        return results;
    });
}

IAsyncOperationWithProgress<IVector<int>^, double>^ Primes::GetPrimesWithProgressAsync(int first, int last)
{
    return create_async([this, first, last](progress_reporter<double> reporter) -> IVector<int>^
    {
        // Ensure that the input values are in range.
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        concurrent_vector<int> primes;
        long operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;
        parallel_for(first, last + 1, [&primes, &operation, range, &lastPercent, reporter](int n)
        {
            // Report progress message.
            double progress = 100.0 * (++operation) / range;
            if (progress >= lastPercent)
            {
                reporter.report(progress);
                lastPercent += 1.0;
            }

            // If the value is prime, add it to the local vector. 
            if (is_prime(n))
            {
                primes.push_back(n);
            }
        });
        reporter.report(100.0);

        // Sort the results.
        sort(begin(primes), end(primes), less<int>());

        // Copy the results to an IVector object. The IVector 
        // interface makes collections of data available to other 
        // Windows Runtime components.
        auto results = ref new Vector<int>();
        for (int prime : primes)
        {
            results->Append(prime);
        }
        return results;
    });
}

Minden metódus először érvényesíti, hogy a bemeneti paraméterek ne legyenek negatívak. Ha egy bemeneti érték negatív, a metódus Platform::InvalidArgumentException-t dob. A hibakezelést a szakasz későbbi részében ismertetik.

Ha ezeket a módszereket egy UWP-alkalmazásból szeretné felhasználni, a Visual C# Blank App (XAML) sablonnal vegyen fel egy második projektet a Visual Studio-megoldásba. Ez a példa a projekt Primesnevét tartalmazza. Ezután a Primes projektből adjon hozzá egy hivatkozást a PrimesLibrary projekthez.

Adja hozzá a következő kódot a MainPage.xaml fájlhoz. Ez a kód határozza meg a felhasználói felületet, hogy meghívhassa a C++ összetevőt, és megjeleníthesse az eredményeket.

<Page
    x:Class="Primes.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Primes"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"/>
            <ColumnDefinition Width="300"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="125"/>
            <RowDefinition Height="125"/>
            <RowDefinition Height="125"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Column="0" Grid.Row="0">
            <Button Name="b1" Click="computePrimes">Compute Primes</Button>
            <TextBlock Name="tb1"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="1" Grid.Row="0">
            <Button Name="b2" Click="computePrimesWithProgress">Compute Primes with Progress</Button>
            <ProgressBar Name="pb1" HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb2"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="0" Grid.Row="1">
            <Button Name="b3" Click="getPrimes">Get Primes</Button>
            <TextBlock Name="tb3"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="1" Grid.Row="1">
            <Button Name="b4" Click="getPrimesWithProgress">Get Primes with Progress</Button>
            <ProgressBar Name="pb4"  HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb4"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="0" Grid.Row="2">
            <Button Name="b5" Click="getPrimesHandleErrors">Get Primes and Handle Errors</Button>
            <ProgressBar Name="pb5"  HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb5"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="1" Grid.Row="2">
            <Button Name="b6" Click="getPrimesCancellation">Get Primes with Cancellation</Button>
            <Button Name="cancelButton" Click="cancelGetPrimes" IsEnabled="false">Cancel</Button>
            <ProgressBar Name="pb6"  HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb6"></TextBlock>
        </StackPanel>
    </Grid>
</Page>

Adja hozzá a következő kódot a MainPage MainPage.xaml osztályhoz. Ez a kód definiál egy Primes objektumot és a gombesemény-kezelőket.

private PrimesLibrary.Primes primesLib = new PrimesLibrary.Primes();

private async void computePrimes(object sender, RoutedEventArgs e)
{
    b1.IsEnabled = false;
    tb1.Text = "Working...";

    var asyncAction = primesLib.ComputePrimesAsync(0, 100000);

    await asyncAction;

    tb1.Text = "Done";
    b1.IsEnabled = true;
}

private async void computePrimesWithProgress(object sender, RoutedEventArgs e)
{
    b2.IsEnabled = false;
    tb2.Text = "Working...";

    var asyncAction = primesLib.ComputePrimesWithProgressAsync(0, 100000);
    asyncAction.Progress = new AsyncActionProgressHandler<double>((action, progress) =>
    {
        pb1.Value = progress;
    });

    await asyncAction;

    tb2.Text = "Done";
    b2.IsEnabled = true;
}

private async void getPrimes(object sender, RoutedEventArgs e)
{
    b3.IsEnabled = false;
    tb3.Text = "Working...";

    var asyncOperation = primesLib.GetPrimesAsync(0, 100000);

    await asyncOperation;

    tb3.Text = "Found " + asyncOperation.GetResults().Count + " primes";
    b3.IsEnabled = true;
}

private async void getPrimesWithProgress(object sender, RoutedEventArgs e)
{
    b4.IsEnabled = false;
    tb4.Text = "Working...";

    var asyncOperation = primesLib.GetPrimesWithProgressAsync(0, 100000);
    asyncOperation.Progress = new AsyncOperationProgressHandler<IList<int>, double>((operation, progress) =>
    {
        pb4.Value = progress;
    });

    await asyncOperation;

    tb4.Text = "Found " + asyncOperation.GetResults().Count + " primes";
    b4.IsEnabled = true;
}

private async void getPrimesHandleErrors(object sender, RoutedEventArgs e)
{
    b5.IsEnabled = false;
    tb5.Text = "Working...";

    var asyncOperation = primesLib.GetPrimesWithProgressAsync(-1000, 100000);
    asyncOperation.Progress = new AsyncOperationProgressHandler<IList<int>, double>((operation, progress) =>
    {
        pb5.Value = progress;
    });

    try
    {
        await asyncOperation;
        tb5.Text = "Found " + asyncOperation.GetResults().Count + " primes";
    }
    catch (ArgumentException ex)
    {
        tb5.Text = "ERROR: " + ex.Message;
    }

    b5.IsEnabled = true;
}

private IAsyncOperationWithProgress<IList<int>, double> asyncCancelableOperation;

private async void getPrimesCancellation(object sender, RoutedEventArgs e)
{
    b6.IsEnabled = false;
    cancelButton.IsEnabled = true;
    tb6.Text = "Working...";

    asyncCancelableOperation = primesLib.GetPrimesWithProgressAsync(0, 200000);
    asyncCancelableOperation.Progress = new AsyncOperationProgressHandler<IList<int>, double>((operation, progress) =>
    {
        pb6.Value = progress;
    });

    try
    {
        await asyncCancelableOperation;
        tb6.Text = "Found " + asyncCancelableOperation.GetResults().Count + " primes";
    }
    catch (System.Threading.Tasks.TaskCanceledException)
    {
        tb6.Text = "Operation canceled";
    }

    b6.IsEnabled = true;
    cancelButton.IsEnabled = false;
}

private void cancelGetPrimes(object sender, RoutedEventArgs e)
{
    cancelButton.IsEnabled = false;
    asyncCancelableOperation.Cancel();
}

Ezek a metódusok a async és await kulcsszavak használatával frissítik a felhasználói felületet az aszinkron műveletek befejezése után. Az UWP-alkalmazások aszinkron kódolásával kapcsolatos információkért lásd: Szálkezelés és aszinkron programozás.

A getPrimesCancellation metódusok és cancelGetPrimes a metódusok együttműködve lehetővé teszik a felhasználó számára a művelet megszakítását. Amikor a felhasználó a Mégse gombot választja, a metódus cancelGetPrimes parancsot a művelet megszakításához.< Az alapul szolgáló aszinkron műveletet kezelő egyidejűségi futtatókörnyezet egy belső kivételtípust küld, amelyet a Windows futtatókörnyezet fog le, hogy közölje, hogy a törlés befejeződött. A lemondási modellről további információt a Lemondás című témakörben talál.

Fontos

Ha azt szeretné elérni, hogy a PPL megfelelően jelentse a Windows futtatókörnyezetnek, hogy megszakította a műveletet, ne kezelje ezt a belső kivételtípust. Ez azt jelenti, hogy nem kell minden kivételt (catch (...)) elkapnia. Ha minden kivételt el kell kapnia, a kivételt újból meg kell adnia, hogy a Windows futtatókörnyezet végrehajthassa a lemondási műveletet.

Az alábbi ábra az Primes alkalmazást mutatja, miután minden egyes beállítási lehetőség kiválasztásra került.

Windows Runtime Primes alkalmazás.

A más nyelvek által használható aszinkron feladatok létrehozására szolgáló create_async példa: A C++ használata a Bing Maps Trip Optimizer-mintában.

A végrehajtási szál vezérlése

A Windows futtatókörnyezet a COM szálmodellt használja. Ebben a modellben az objektumok különböző apartmanokban vannak üzemeltetve, attól függően, hogy hogyan kezelik a szinkronizálásukat. A szálbiztos objektumokat a többszálas lakásban (MTA) üzemeltetik. Azokat az objektumokat, amelyeket egyetlen szálnak kell elérnie, egyszálas lakásban (STA) kell üzemeltetni.

A felhasználói felülettel rendelkező alkalmazásokban az ASTA (Application STA) szál felel az ablaküzenetek szivattyúzásáért, és ez az egyetlen szál a folyamatban, amely képes frissíteni az STA által üzemeltetett felhasználói felület vezérlőit. Ennek két következménye van. Először is, ahhoz, hogy az alkalmazás rugalmas maradjon, nem szabad minden processzorigényes és I/O-műveletet futtatni az ASTA-szálon. Másodszor, a felhasználói felület frissítéséhez a háttérszálakból származó eredményeket vissza kell adni az ASTA-ba. Egy C++ UWP-alkalmazásban az MainPage és más XAML-oldalak mind az ATSA-n futnak. Ezért az ASTA-n deklarált feladat-folytatások alapértelmezés szerint ott futnak, így közvetlenül a folytatás törzsében frissítheti a vezérlőket. Ha azonban egy tevékenységet egy másik tevékenységbe ágyaz be, a beágyazott tevékenység folytatásai az MTA-ban futnak. Ezért meg kell fontolnia, hogy explicit módon adja-e meg, hogy milyen környezetben futnak ezek a folytatások.

Az aszinkron műveletből létrehozott feladatok, például IAsyncOperation<TResult>speciális szemantikát használnak, amelyek segíthetnek figyelmen kívül hagyni a szálkezelés részleteit. Bár egy művelet futtatható egy háttérszálon (vagy egyáltalán nem biztos, hogy egy szál alátámasztja), a folytatások alapértelmezés szerint garantáltan azon a lakáson futnak, amely elindította a folytatási műveleteket (más szóval a hívott task::thenlakásból). A concurrency::task_continuation_context osztály használatával szabályozhatja a folytatás végrehajtási környezetét. Az alábbi statikus segédmetódusokkal hozhat létre task_continuation_context objektumokat:

Átadhat egy task_continuation_context objektumot a task::then metódusnak, hogy explicit módon szabályozza a folytatás végrehajtási környezetét, vagy átadhatja a feladatot egy másik kontextusnak, majd meghívhatja a task::then metódust, hogy implicit módon szabályozza a végrehajtási környezetet.

Fontos

Mivel az UWP-alkalmazások fő felhasználói felületi szála az STA alatt fut, a sta-on létrehozott folytatások alapértelmezés szerint az adott sta-on futnak. Ennek megfelelően az MTA-n létrehozott folytatásokat az MTA-n futtatják.

Az alábbi szakasz egy olyan alkalmazást mutat be, amely lemezről olvas be egy fájlt, megkeresi a fájl leggyakoribb szavait, majd megjeleníti az eredményeket a felhasználói felületen. A felhasználói felület frissítésének utolsó művelete a felhasználói felület szálán történik.

Fontos

Ez a viselkedés az UWP-alkalmazásokra jellemző. Asztali alkalmazások esetén nem szabályozhatja, hogy hol futnak a folytatások. Ehelyett az ütemező kiválaszt egy munkaszálat, amelyen az egyes folytatásokat futtatja.

Fontos

Ne hívja meg a concurrency::task::wait-ot az STA-n futó folytatás törzsében. Ellenkező esetben a futtatókörnyezet egyidejűséget eredményez::invalid_operation mivel ez a módszer blokkolja az aktuális szálat, és az alkalmazás nem válaszolhat. Az egyidejűség::task::get metódus meghívásával azonban megkaphatja az előzményfeladat eredményét egy tevékenységalapú folytatásban.

Példa: Végrehajtás szabályozása windowsos futtatókörnyezeti alkalmazásban C++ és XAML használatával

Fontolja meg egy C++ XAML-alkalmazást, amely beolvassa a lemezről a fájlt, megkeresi a fájl leggyakoribb szavait, majd megjeleníti az eredményeket a felhasználói felületen. Az alkalmazás létrehozásához először hozzon létre a Visual Studióban egy Üres alkalmazás (Univerzális Windows) projektet, és nevezze elCommonWords. Az alkalmazásjegyzékben adja meg a Dokumentumtár funkciót, amely lehetővé teszi az alkalmazás számára a Dokumentumok mappa elérését. Adja hozzá a Szöveg (.txt) fájltípust is az alkalmazásjegyzék deklarációk szakaszához. Az alkalmazás képességeivel és deklarációival kapcsolatos további információkért lásd a Windows-alkalmazások csomagolását, üzembe helyezését és lekérdezését.

Frissítse a Grid elemet a MainPage.xaml fájlban úgy, hogy tartalmazzon egy ProgressRing elemet és egy TextBlock elemet. Ez ProgressRing azt jelzi, hogy a művelet folyamatban van, és a TextBlock számítás eredményeit jeleníti meg.

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <ProgressRing x:Name="Progress"/>
    <TextBlock x:Name="Results" FontSize="16"/>
</Grid>

Adja hozzá a következő #include utasításokat a pch.h fájlhoz.

#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>

Adja hozzá a következő metódusdeklarációkat az MainPage osztályhoz (MainPage.h).

private:
    // Splits the provided text string into individual words.
    concurrency::task<std::vector<std::wstring>> MakeWordList(Platform::String^ text);

    // Finds the most common words that are at least the provided minimum length.
    concurrency::task<std::vector<std::pair<std::wstring, size_t>>> FindCommonWords(const std::vector<std::wstring>& words, size_t min_length, size_t count);

    // Shows the most common words on the UI.
    void ShowResults(const std::vector<std::pair<std::wstring, size_t>>& commonWords);

Adja hozzá a következő using utasításokat a MainPage.cpp.

using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;

A MainPage.cpp implementálja a MainPage::MakeWordList, MainPage::FindCommonWordsés MainPage::ShowResults a metódusokat. A MainPage::MakeWordList és a MainPage::FindCommonWords számításigényes műveleteket hajtanak végre. A MainPage::ShowResults metódus megjeleníti a számítás eredményét a felhasználói felületen.

// Splits the provided text string into individual words.
task<vector<wstring>> MainPage::MakeWordList(String^ text)
{
    return create_task([text]() -> vector<wstring>
    {
        vector<wstring> words;

        // Add continuous sequences of alphanumeric characters to the string vector.
        wstring current_word;
        for (wchar_t ch : text)
        {
            if (!iswalnum(ch))
            {
                if (current_word.length() > 0)
                {
                    words.push_back(current_word);
                    current_word.clear();
                }
            }
            else
            {
                current_word += ch;
            }
        }

        return words;
    });
}

// Finds the most common words that are at least the provided minimum length.
task<vector<pair<wstring, size_t>>> MainPage::FindCommonWords(const vector<wstring>& words, size_t min_length, size_t count)
{
    return create_task([words, min_length, count]() -> vector<pair<wstring, size_t>>
    {
        typedef pair<wstring, size_t> pair;

        // Counts the occurrences of each word.
        concurrent_unordered_map<wstring, size_t> counts;

        parallel_for_each(begin(words), end(words), [&counts, min_length](const wstring& word)
        {
            // Increment the count of words that are at least the minimum length. 
            if (word.length() >= min_length)
            {
                // Increment the count.
                InterlockedIncrement(&counts[word]);
            }
        });

        // Copy the contents of the map to a vector and sort the vector by the number of occurrences of each word.
        vector<pair> wordvector;
        copy(begin(counts), end(counts), back_inserter(wordvector));

        sort(begin(wordvector), end(wordvector), [](const pair& x, const pair& y)
        {
            return x.second > y.second;
        });

        size_t size = min(wordvector.size(), count);
        wordvector.erase(begin(wordvector) + size, end(wordvector));

        return wordvector;
    });
}

// Shows the most common words on the UI. 
void MainPage::ShowResults(const vector<pair<wstring, size_t>>& commonWords)
{
    wstringstream ss;
    ss << "The most common words that have five or more letters are:";
    for (auto commonWord : commonWords)
    {
        ss << endl << commonWord.first << L" (" << commonWord.second << L')';
    }

    // Update the UI.
    Results->Text = ref new String(ss.str().c_str());
}

Módosítsa a MainPage konstruktort a folytatási feladatok láncának létrehozására, amely a felhasználói felületen megjeleníti a Homer által az Iliad című könyvben szereplő gyakori szavakat. Az első két folytatási feladat, amely a szöveget különálló szavakra osztja, és közös szavakat keres, időigényes lehet, ezért kifejezetten a háttérben való futtatásra van beállítva. Az utolsó folytatási feladat, amely frissíti az UI-t, nem határoz meg folytatási környezetet, ezért követi a komponensszál-kezelési szabályokat.

MainPage::MainPage()
{
    InitializeComponent();

    // To run this example, save the contents of http://www.gutenberg.org/files/6130/6130-0.txt to your Documents folder.
    // Name the file "The Iliad.txt" and save it under UTF-8 encoding.

    // Enable the progress ring.
    Progress->IsActive = true;

    // Find the most common words in the book "The Iliad".

    // Get the file.
    create_task(KnownFolders::DocumentsLibrary->GetFileAsync("The Iliad.txt")).then([](StorageFile^ file)
    {
        // Read the file text.
        return FileIO::ReadTextAsync(file, UnicodeEncoding::Utf8);

        // By default, all continuations from a Windows Runtime async operation run on the 
        // thread that calls task.then. Specify use_arbitrary to run this continuation 
        // on a background thread.
    }, task_continuation_context::use_arbitrary()).then([this](String^ file)
    {
        // Create a word list from the text.
        return MakeWordList(file);

        // By default, all continuations from a Windows Runtime async operation run on the 
        // thread that calls task.then. Specify use_arbitrary to run this continuation 
        // on a background thread.
    }, task_continuation_context::use_arbitrary()).then([this](vector<wstring> words)
    {
        // Find the most common words.
        return FindCommonWords(words, 5, 9);

        // By default, all continuations from a Windows Runtime async operation run on the 
        // thread that calls task.then. Specify use_arbitrary to run this continuation 
        // on a background thread.
    }, task_continuation_context::use_arbitrary()).then([this](vector<pair<wstring, size_t>> commonWords)
    {
        // Stop the progress ring.
        Progress->IsActive = false;

        // Show the results.
        ShowResults(commonWords);

        // We don't specify a continuation context here because we want the continuation 
        // to run on the STA thread.
    });
}

Megjegyzés:

Ez a példa bemutatja, hogyan adhatja meg a végrehajtási környezeteket, és hogyan hozhat létre folytatási láncot. Ne feledje, hogy alapértelmezés szerint egy aszinkron műveletből létrehozott feladat a folytatásokat azon az apartmanon futtatja, amelyik hívta a task::then-t. Ezért ez a példa azt határozza task_continuation_context::use_arbitrary meg, hogy a felhasználói felületet nem tartalmazó műveletek háttérszálon legyenek végrehajtva.

Az alábbi ábra CommonWords az alkalmazás eredményeit mutatja.

Windows Runtime CommonWords alkalmazás.

Ebben a példában lehet támogatni a lemondást, mert a task objektumok, amelyek a create_async támogatják, implicit leállítási jogkivonatot használnak. Adja meg a munkafüggvényét úgy, hogy elfogadjon egy cancellation_token objektumot, ha a feladatoknak együttműködően kell válaszolniuk a törlésre. További információ a PPL-ben történő lemondásról: Lemondás a PPL-ben

Lásd még

Párhuzamossági futtatókörnyezet