Vytváření asynchronních operací v jazyce C++ pro aplikace pro UPW

Tento dokument popisuje některé klíčové body, které je třeba mít na paměti při použití třídy úloh k vytváření asynchronních operací založených na Technologii Windows ThreadPool v aplikaci Universal prostředí Windows Runtime (UPW).

Použití asynchronního programování je klíčovou komponentou modelu aplikace prostředí Windows Runtime, protože umožňuje aplikacím zůstat reagující na uživatelský vstup. Dlouhotrvající úlohu můžete spustit bez blokování vlákna uživatelského rozhraní a později můžete získat výsledky úlohy. Můžete také zrušit úkoly a přijímat oznámení o průběhu jako úkoly spouštěné na pozadí. Dokument Asynchronní programování v jazyce C++ poskytuje přehled asynchronního vzoru, který je k dispozici v jazyce Visual C++ k vytváření aplikací pro UPW. Tento dokument se naučí, jak využívat a vytvářet řetězy asynchronních prostředí Windows Runtime operací. Tato část popisuje, jak používat typy v ppltasks.h k vytváření asynchronních operací, které lze využívat jinou komponentou prostředí Windows Runtime a jak řídit, jak se provádí asynchronní práce. Zvažte také čtení vzorů asynchronního programování a tipů v Hilo (aplikace pro Windows Store pomocí C++ a XAML) a zjistěte, jak jsme pomocí třídy úloh implementovali asynchronní operace v Hilu, prostředí Windows Runtime aplikaci pomocí C++ a XAML.

Poznámka

Knihovnu PPL (Parallel Patterns Library) a asynchronní agenty můžete použít v aplikaci pro UPW. Plánovač úloh ani Správce zdrojů však nemůžete použít. Tento dokument popisuje další funkce, které PPL poskytuje, které jsou k dispozici pouze pro aplikaci pro UPW, a ne pro desktopovou aplikaci.

Klíčové body

  • Použití souběžnosti::create_async k vytvoření asynchronních operací, které lze použít jinými komponentami (které mohou být napsané v jiných jazycích než C++).

  • Pomocí concurrency::p rogress_reporter můžete hlásit oznámení o průběhu komponentám, které volají vaše asynchronní operace.

  • Pomocí tokenů zrušení povolte zrušení interní asynchronní operace.

  • Chování create_async funkce závisí na návratovém typu pracovní funkce, která je jí předána. Pracovní funkce, která vrací úlohu (nebo task<T>task<void>) synchronně v kontextu, který volal create_async. Pracovní funkce, která vrací T nebo void běží v libovolném kontextu.

  • Pomocí metody concurrency::task::then můžete vytvořit řetěz úkolů, které se spouští jeden po druhém. Výchozí kontext pokračování úkolu v aplikaci pro UPW závisí na tom, jak byl úkol vytvořen. Pokud byl úkol vytvořen předáním asynchronní akce konstruktoru úkolu nebo předáním výrazu lambda, který vrací asynchronní akci, pak výchozí kontext pro všechna pokračování tohoto úkolu je aktuální kontext. Pokud úkol není sestaven z asynchronní akce, použije se pro pokračování úkolu ve výchozím nastavení libovolný kontext. Výchozí kontext můžete přepsat pomocí třídy concurrency::task_continuation_context .

V tomto dokumentu

Vytváření asynchronních operací

Model úloh a pokračování můžete použít v knihovně PPL (Parallel Patterns Library) k definování úloh na pozadí a dalších úloh, které se spouští při dokončení předchozí úlohy. Tuto funkci poskytuje třída concurrency::task . Další informace o tomto modelu a task třídě naleznete v tématu Paralelismus úloh.

Prostředí Windows Runtime je programovací rozhraní, které můžete použít k vytváření aplikací pro UPW, které běží pouze ve speciálním prostředí operačního systému. Tyto aplikace používají autorizované funkce, datové typy a zařízení a distribuují se z Microsoft Storu. Prostředí Windows Runtime je reprezentována binárním rozhraním aplikace (ABI). ABI je základní binární kontrakt, který zpřístupňuje prostředí Windows Runtime rozhraní API pro programovací jazyky, jako je Visual C++.

Pomocí prostředí Windows Runtime můžete použít nejlepší funkce různých programovacích jazyků a kombinovat je do jedné aplikace. Můžete například vytvořit uživatelské rozhraní v JavaScriptu a provádět výpočetní logiku aplikace v komponentě C++. Schopnost provádět tyto výpočetní operace na pozadí je klíčovým faktorem zachování odezvy uživatelského rozhraní. Vzhledem k tomu, že task třída je specifická pro jazyk C++, musíte použít rozhraní prostředí Windows Runtime ke komunikaci asynchronních operací s jinými komponentami (které mohou být zapsány v jiných jazycích než C++). Prostředí Windows Runtime poskytuje čtyři rozhraní, která můžete použít k reprezentaci asynchronních operací:

Windows::Foundation::IAsyncAction
Představuje asynchronní akci.

Windows::Foundation::IAsyncActionWithProgress TProgress<>
Představuje asynchronní akci, která hlásí průběh.

Windows::Foundation::IAsyncOperation<TResult>
Představuje asynchronní operaci, která vrací výsledek.

Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Představuje asynchronní operaci, která vrací výsledek a hlásí průběh.

Pojem akce znamená, že asynchronní úloha nevygeneruje hodnotu (představte si funkci, která vrací void). Pojem operace znamená, že asynchronní úloha vytvoří hodnotu. Pojem průběhu znamená, že úkol může hlásit zprávy o průběhu volajícímu. JavaScript, .NET Framework a Visual C++ každý z nich nabízí vlastní způsob, jak vytvářet instance těchto rozhraní pro použití v rámci hranice ABI. Pro Visual C++ poskytuje PPL funkci concurrency::create_async . Tato funkce vytvoří prostředí Windows Runtime asynchronní akci nebo operaci, která představuje dokončení úkolu. Funkce create_async přebírá pracovní funkci (obvykle výraz lambda), interně vytvoří task objekt a zabalí tento úkol do jednoho ze čtyř asynchronních prostředí Windows Runtime rozhraní.

Poznámka

Používejte create_async pouze tehdy, když potřebujete vytvořit funkce, ke kterým je možné přistupovat z jiného jazyka nebo jiné komponenty prostředí Windows Runtime. task Třídu použijte přímo, když víte, že operace je vytvořena a spotřebována kódem C++ ve stejné komponentě.

Návratový create_async typ je určen typem argumentů. Pokud například vaše pracovní funkce nevrací hodnotu a nehlásí průběh, create_async vrátí hodnotu IAsyncAction. Pokud vaše pracovní funkce nevrací hodnotu a také hlásí průběh, create_async vrátí hodnotu IAsyncActionWithProgress. Pokud chcete hlásit průběh, zadejte jako parametr pracovní funkce objekt concurrency::p rogress_reporter . Schopnost hlásit průběh vám umožní hlásit, kolik práce bylo provedeno a jaké množství stále zůstává (například v procentech). Umožňuje také hlásit výsledky, jakmile budou dostupné.

Rozhraní IAsyncAction, IAsyncActionWithProgress<TProgress>IAsyncOperation<TResult>a IAsyncActionOperationWithProgress<TProgress, TProgress> rozhraní poskytují metoduCancel, která umožňuje zrušit asynchronní operace. Třída task funguje s tokeny zrušení. Pokud ke zrušení práce použijete token zrušení, modul runtime nespustí novou práci, která se přihlásí k odběru daného tokenu. Práce, která je již aktivní, může monitorovat svůj token zrušení a zastavit, když je to možné. Tento mechanismus je podrobněji popsán v dokumentu Zrušení v PPL. Zrušení úlohy můžete propojit pomocí metod prostředí Windows Runtime Cancel dvěma způsoby. Nejprve můžete definovat pracovní funkci, kterou předáte create_async , aby převzala objekt concurrency::cancellation_token . Cancel Při volání metody je tento token zrušení zrušen a normální pravidla zrušení se vztahují na základní task objekt, který podporuje create_async volání. Pokud objekt nezadáte cancellation_token , podkladový task objekt ho implicitně definuje. cancellation_token Definujte objekt, když potřebujete spolupracovat na zrušení ve své pracovní funkci. Příklad oddílu: Řízení provádění v aplikaci prostředí Windows Runtime pomocí C++ a XAML ukazuje příklad provedení zrušení v aplikaci Univerzální platforma Windows (UPW) s C# a XAML, který používá vlastní prostředí Windows Runtime komponentu C++.

Upozorňující

V řetězci pokračování úloh vždy vyčistíte stav a potom zavoláte souběžnost::cancel_current_task při zrušení tokenu zrušení. Pokud se místo volání cancel_current_taskvrátíte brzy, operace přejde do dokončeného stavu místo zrušeného stavu.

Následující tabulka shrnuje kombinace, které můžete použít k definování asynchronních operací v aplikaci.

Vytvoření tohoto rozhraní prostředí Windows Runtime Vrátit tento typ z create_async Předáním těchto typů parametrů pracovní funkci použijete implicitní token zrušení. Předání těchto typů parametrů do pracovní funkce k použití explicitního tokenu zrušení
IAsyncAction void nebo task<void> (žádný) (cancellation_token)
IAsyncActionWithProgress<TProgress> void nebo task<void> (progress_reporter) (progress_reporter, cancellation_token)
IAsyncOperation<TResult> T nebo task<T> (žádný) (cancellation_token)
IAsyncActionOperationWithProgress<TProgress, TProgress> T nebo task<T> (progress_reporter) (progress_reporter, cancellation_token)

Hodnotu nebo task objekt můžete vrátit z pracovní funkce, kterou funkci předáte create_async . Tyto varianty vytvářejí různá chování. Když vrátíte hodnotu, pracovní funkce se zabalí do task vlákna na pozadí, aby ji bylo možné spustit na vlákně na pozadí. Kromě toho podkladový task token používá implicitní token zrušení. Naopak, pokud vrátíte task objekt, pracovní funkce běží synchronně. Proto pokud vrátíte task objekt, ujistěte se, že všechny zdlouhavé operace ve vaší pracovní funkci běží také jako úlohy, aby aplikace zůstala responzivní. Kromě toho podkladový task token nepoužívá implicitní token zrušení. Proto je třeba definovat svou pracovní funkci, aby převzal cancellation_token objekt, pokud požadujete podporu zrušení při vrácení objektu task z create_async.

Následující příklad ukazuje různé způsoby vytvoření objektuIAsyncAction, který může využívat jiná komponenta prostředí Windows Runtime.

// 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říklad: Vytvoření komponenty prostředí Windows Runtime jazyka C++ a jeho využití z jazyka C#

Zvažte aplikaci, která k definování uživatelského rozhraní a komponenty prostředí Windows Runtime jazyka C++, která používá XAML a C# k provádění operací náročných na výpočetní výkon. V tomto příkladu komponenta C++ vypočítá, která čísla v daném rozsahu jsou prime. Chcete-li znázornit rozdíly mezi čtyřmi prostředí Windows Runtime asynchronními rozhraními úloh, začněte v sadě Visual Studio vytvořením prázdného řešení a jeho pojmenováním Primes. Pak do řešení přidejte projekt komponenty prostředí Windows Runtime a pojmete ho PrimesLibrary. Do vygenerovaného souboru hlavičky C++ přidejte následující kód (v tomto příkladu se přejmenuje Třída1.h na Primes.h). Každá public metoda definuje jedno ze čtyř asynchronních rozhraní. Metody, které vrací hodnotu vrátí Windows::Foundation::Collections::IVector<int> objekt. Metody, které hlásí průběh, vytvářejí double hodnoty, které definují procento celkové práce, která byla dokončena.

#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);
    };
}

Poznámka

Podle konvence názvy asynchronních metod v prostředí Windows Runtime obvykle končí na "Async".

Do vygenerovaného zdrojového souboru C++ přidejte následující kód (tento příklad přejmenuje Class1.cpp na Primes.cpp). Funkce is_prime určuje, zda je jeho vstup prime. Zbývající metody implementují Primes třídu. Každé volání create_async používá podpis, který je kompatibilní s metodou, ze které je volána. Protože Primes::ComputePrimesAsync například vrací IAsyncAction, pracovní funkce, která je zadaná, create_async nevrací hodnotu a jako její parametr nepřebírají progress_reporter objekt.

// 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;
    });
}

Každá metoda nejprve provede ověření, aby se zajistilo, že vstupní parametry nejsou záporné. Pokud je vstupní hodnota záporná, metoda vyvolá Platform::InvalidArgumentException. Zpracování chyb je vysvětleno dále v této části.

Pokud chcete tyto metody využívat z aplikace pro UPW, použijte šablonu Visual C# Blank App (XAML) k přidání druhého projektu do řešení sady Visual Studio. Tento příklad pojmenuje projekt Primes. Potom z Primes projektu přidejte odkaz na PrimesLibrary projekt.

Do souboru MainPage.xaml přidejte následující kód. Tento kód definuje uživatelské rozhraní, abyste mohli volat komponentu C++ a zobrazit výsledky.

<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>

Do třídy v MainPage.xaml přidejte následující kód MainPage . Tento kód definuje Primes objekt a obslužné rutiny událostí tlačítka.

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();
}

Tyto metody používají async klíčová slova a await aktualizují uživatelské rozhraní po dokončení asynchronních operací. Informace o asynchronním kódování v aplikacích pro UPW najdete v tématu Threading a asynchronní programování.

Společně getPrimesCancellation fungují a cancelGetPrimes umožňují uživateli zrušit operaci. Když uživatel zvolí tlačítko Storno , cancelGetPrimes metoda volá IAsyncOperationWithProgress<TResult, TProgress>::Cancel pro zrušení operace. Modul Concurrency Runtime, který spravuje základní asynchronní operaci, vyvolá interní typ výjimky zachycený prostředí Windows Runtime ke sdělení, že zrušení bylo dokončeno. Další informace o modelu zrušení naleznete v tématu Zrušení.

Důležité

Pokud chcete PPL povolit správné hlášení prostředí Windows Runtime, že operaci zrušil, nezachyťte tento interní typ výjimky. To znamená, že byste neměli zachytávat všechny výjimky (catch (...)). Pokud musíte zachytávat všechny výjimky, znovu vyřešte výjimku, abyste zajistili, že prostředí Windows Runtime může dokončit operaci zrušení.

Následující obrázek znázorňuje Primes aplikaci po výběru jednotlivých možností.

Windows Runtime Primes app.

Příklad, který používá create_async k vytvoření asynchronních úloh, které můžou využívat jiné jazyky, najdete v tématu Použití jazyka C++ v ukázce optimalizátoru cest Bingu Mapy.

Řízení spouštěcího vlákna

Prostředí Windows Runtime používá model vláken modelu COM. V tomto modelu jsou objekty hostovány v různých apartmánech v závislosti na tom, jak zpracovávají synchronizaci. Objekty bezpečné pro vlákna jsou hostovány v apartmánu s více vlákny (MTA). Objekty, ke kterým musí přistupovat jedno vlákno, se hostují v apartmánu s jedním vláknem (STA).

V aplikaci, která má uživatelské rozhraní, je vlákno ASTA (APPLICATION STA) zodpovědné za pumpování zpráv oken a je jediným vláknem v procesu, který může aktualizovat ovládací prvky uživatelského rozhraní hostovaného sta. To má dva důsledky. Za prvé, aby aplikace zůstala responzivní, neměly by se všechny operace náročné na procesor a vstupně-výstupní operace spouštět ve vlákně ASTA. Za druhé, výsledky, které pocházejí z vláken na pozadí, musí být zařazovány zpět do ASTA, aby bylo možné aktualizovat uživatelské rozhraní. V aplikaci MainPage pro UPW C++ a na dalších stránkách XAML se všechny spouštějí v ATSA. Proto jsou pokračování úloh deklarované na ASTA spuštěny ve výchozím nastavení, takže můžete aktualizovat ovládací prvky přímo v textu pokračování. Pokud však úlohu vnořete do jiného úkolu, všechny pokračování v této vnořené úloze se spustí v MTA. Proto je potřeba zvážit, jestli explicitně určit, v jakém kontextu se tato pokračování spustí.

Úloha vytvořená z asynchronní operace, například IAsyncOperation<TResult>, používá speciální sémantiku, která vám může pomoct ignorovat podrobnosti o vláknech. I když může operace běžet na vlákně na pozadí (nebo nemusí být podporována vláknem vůbec), jeho pokračování jsou ve výchozím nastavení zaručeno, že běží v bytě, který spustil operace pokračování (jinými slovy, z bytu, který volal task::then). Ke kontrole kontextu provádění pokračování můžete použít třídu concurrency::task_continuation_context . K vytváření task_continuation_context objektů použijte tyto statické pomocné metody:

Objekt můžete předat task_continuation_contextúkolu::then metoda explicitně řídit kontext spuštění pokračování nebo můžete předat úkol do jiného apartmánu a pak volat metodu task::then implicitně řídit kontext spuštění.

Důležité

Vzhledem k tomu, že hlavní vlákno uživatelského rozhraní aplikací pro UPW běží v rámci STA, pokračování, která vytvoříte na sta ve výchozím nastavení, běží na STA. Proto pokračování, která vytvoříte v MTA, běží na MTA.

Následující část ukazuje aplikaci, která načítá soubor z disku, najde nejběžnější slova v tomto souboru a pak zobrazí výsledky v uživatelském rozhraní. Konečná operace, aktualizace uživatelského rozhraní, nastane ve vlákně uživatelského rozhraní.

Důležité

Toto chování je specifické pro aplikace pro UPW. U desktopových aplikací neřídíte, kde běží pokračování. Místo toho plánovač zvolí pracovní vlákno, na kterém se má jednotlivé pokračování spouštět.

Důležité

Nevolejte souběžnost::task::wait v těle pokračování, které běží na STA. Jinak modul runtime vyvolá souběžnost::invalid_operation , protože tato metoda blokuje aktuální vlákno a může způsobit, že aplikace přestane reagovat. Můžete však volat metodu concurrency::task::get , která obdrží výsledek úlohy stecedent v pokračování založeném na úkolu.

Příklad: Řízení provádění v aplikaci prostředí Windows Runtime pomocí C++ a XAML

Představte si aplikaci C++ XAML, která čte soubor z disku, najde nejběžnější slova v tomto souboru a pak zobrazí výsledky v uživatelském rozhraní. Pokud chcete vytvořit tuto aplikaci, spusťte ji v sadě Visual Studio tak, že vytvoříte projekt Prázdná aplikace (Univerzální systém Windows) a pojmete ji CommonWords. V manifestu aplikace určete schopnost knihovny dokumentů, která aplikaci umožní přístup ke složce Dokumenty. Do oddílu deklarací manifestu aplikace přidejte také typ souboru Text (.txt). Další informace o možnostech a deklarací aplikace najdete v tématu Balení, nasazení a dotazování aplikací pro Windows.

Grid Aktualizujte element v MainPage.xaml tak, aby zahrnoval ProgressRing element a TextBlock element. Označuje ProgressRing , že operace probíhá a TextBlock zobrazuje výsledky výpočtu.

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

Do souboru pch.h přidejte následující #include příkazy.

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

Do třídy (MainPage.h) přidejte následující deklarace MainPage metody.

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);

Do souboru MainPage.cpp přidejte následující using příkazy.

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

V MainPage.cpp implementujte MainPage::MakeWordList, MainPage::FindCommonWordsa MainPage::ShowResults metody. Operace MainPage::MakeWordList náročné na výpočty a MainPage::FindCommonWords provádění operací. Metoda MainPage::ShowResults zobrazí výsledek výpočtu v uživatelském rozhraní.

// 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());
}

MainPage Upravte konstruktor tak, aby vytvořil řetěz úloh pokračování, které se zobrazí v uživatelském rozhraní běžná slova v knize The Iliad by Homer. První dva úkoly pokračování, které rozdělují text na jednotlivá slova a najdou běžná slova, můžou být časově náročné, a proto jsou explicitně nastavené tak, aby běžely na pozadí. Konečná úloha pokračování, která aktualizuje uživatelské rozhraní, určuje žádný kontext pokračování, a proto se řídí pravidly dělení na vlákna bytu.

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.
    });
}

Poznámka

Tento příklad ukazuje, jak určit kontexty spuštění a jak vytvořit řetězec pokračování. Vzpomeňte si, že ve výchozím nastavení úloha vytvořená z asynchronní operace spouští pokračování v bytě, který volal task::then. Tento příklad proto používá task_continuation_context::use_arbitrary k určení, že operace, které nezahrnují uživatelské rozhraní, se provádí ve vlákně na pozadí.

Následující obrázek znázorňuje výsledky CommonWords aplikace.

Windows Runtime CommonWords app.

V tomto příkladu je možné podporovat zrušení, protože task objekty, které podporují create_async použití implicitního tokenu zrušení. Definujte svou pracovní funkci tak, aby převzala cancellation_token objekt, pokud vaše úkoly potřebují reagovat na zrušení způsobem spolupráce. Další informace o zrušení v PPL naleznete v tématu Zrušení v PPL

Viz také

Concurrency Runtime