Sdílet prostřednictvím


Vytváření asynchronních operací v jazyce C++ pro aplikace pro web Windows Store

Tento dokument popisuje některé klíčové body, které by měly být brány v úvahu během práce s modulem Concurrency Runtime, aby byly v aplikacích Windows Store vytvořeny asynchronní operace.

Použití asynchronního programování je klíčovou součástí aplikačního modelu Windows Store, protože umožňuje aplikacím neustále reagovat na vstupy uživatele.Dlouhotrvající úlohu lze spustit bez blokování vlákna uživatelského rozhraní a získat výsledky úkolu později.Lze také zrušit úlohy a přijímat oznámení o průběhu úlohy spuštěné na pozadí.Dokument asynchronní programování v jazyce C++ obsahuje přehled o asynchronních vzorech, které jsou k dispozici ve Visual C++ pro vytváření aplikací Windows Store.Tento dokument učí, jak využívat i vytvářet řetězy asynchronních Windows Runtime operací.Tato část popisuje způsob použití modulu Concurrency Runtime pro vytvoření asynchronních operací, které mohou být spotřebovány v jiné komponentě Windows Runtime, a jak ovládat běh asynchronních provádění.Zvažte také přečtení textu o asynchronních programovacích vzorech a tipech v Hilo (aplikace Windows Store pomocí jazyka C++ a XAML), kde se dozvíte, jak jsme modul Concurrency Runtime použili k implementaci asynchronních operací v Hilo, aplikaci Windows Store využívající jazyka C++ a XAML.

[!POZNÁMKA]

Knihovnu paralelních vzorů (PPL) a Asynchronní agenti knihovny lze použít v aplikacích Windows Store.Nicméně však nelze použít Plánovač úloh ani Správce prostředků.Tento dokument popisuje další funkce, které poskytuje modul Concurrency Runtime, které jsou k dispozici pouze pro aplikace Windows Store a nikoli pro aplikace klasické pracovní plochy.

Klíčové body

  • Použití concurrency::create_async k vytvoření asynchronních operací, které lze použít jinými součástmi (které mohou být napsány v jiných jazycích než je C++).

  • Pro vytvoření sestav oznámení o průběhu součástem, které volají asynchronní operace lze použít concurrency::progress_reporter.

  • Pro umožnění zrušení asynchronních operací použijte tokeny zrušení.

  • Chování funkce create_async závisí na typu vrácené hodnoty pracovní funkce, která je ji předána.Pracovní funkce, která vrací úkol (buď task<T> nebo task<void>) pracuje synchronně v kontextu, který se volal create_async.Pracovní funkce, která vrací T nebo void, běží v libovolném kontextu.

  • Pro zřetězení úkolů, aby byly spuštěny jeden za druhým, můžete použít metodu concurrency::task::then.V aplikaci operačního systému Windows Store výchozí kontext pokračování úkolu 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 lambda výrazu, který vrací asynchronní akci, je výchozí kontext pro všechna pokračování tohoto úkolu aktuální kontextu.Pokud není úkol vyroben z asynchronní akce, pak je ve výchozím nastavení použit libovolný kontext pro pokračování úkolu.Výchozí kontext lze přepsat kontext třídou concurrency::task_continuation_context.

V tomto dokumentu

  • Vytvoření asynchronní operace

  • Příklad: Vytvoření součásti modulu Windows Runtime jazyka C++

  • Řízení vlákna provádění

  • Příklad: Řízení provádění ve Windows Store aplikaci s jazykem C++ a XAML

Vytvoření asynchronní operace

Model úkolů a pokračování v knihovně PPL (Parallel Patterns Library) lze použít k definování úkolů na pozadí stejně jako dalších úkolů, které se spustí, když se předchozí úkol dokončí.Tato funkcionality je poskytována třídou concurrency::task.Další informace o tomto modelu a třídě task v tématu Úkol rovnoběžnosti (souběžnosti Runtime).

Windows Runtime je programovací rozhraní, které lze použít k vytvoření Windows Store aplikace, které běží pouze v prostředí speciálního operačního systému.Takovéto aplikace používají oprávněné funkce, typy dat a zařízení a jsou distribuovány z Windows Store.Windows Runtime je reprezentováno binárním rozhraním aplikace (Application Binary Interface - ABI).Rozhraní ABI představuje podkladovou binární smlouvu umožňující aby bylo API rozhraní Windows Runtime k dispozici programovacím jazykům, jako je třeba Visual C++.

Použitím rozhraní Windows Runtime lze využít nejlepší funkce různých programovacích jazyků a sloučit je do jedné aplikace.Lze například vytvořit uživatelské rozhraní v jazyce JavaScript a výpočetně náročnou logiku aplikace provádět v komponentě jazyka C++.Schopnost provádět tyto výpočetně náročné operace na pozadí je klíčovým faktorem toho, aby uživatelské rozhraní reagovalo optimálně.Protože je třída task specifická pro jazyk C++, je nutné pro komunikaci asynchronních operací s dalšími komponentami (které mohou být napsány v jiných jazycích než C++) použít rozhraní Windows Runtime.Windows Runtime poskytuje čtyři rozhraní, která lze použít k reprezentaci asynchronních operací:

Pojem akce znamená, že asynchronní úlohy nevracejí žádnou hodnotu (představte si funkci, která vrací void).Pojem operace znamená, že asynchronní úkol vrací hodnotu.Pojem průběh znamená, že úkol může posílat zprávy o průběhu volajícímu.JavaScript, rozhraní .NET Framework a jazyk Visual C++ poskytují vlastní způsob vytvoření instancí těchto rozhraní pro použití za hranicemi ABI.Pro jazyk Visual C++ poskytuje modul Concurrency Runtime funkci concurrency::create_async.Tato funkce vytvoří asynchronní akci Windows Runtime nebo operaci, která představuje dokončení úkolu.Funkce create_async přebírá funkci práce (obvykle lambda výraz), vnitřně vytvoří objekt task a zabalí jej do jednoho ze čtyř asynchronních rozhraní Windows Runtime.

[!POZNÁMKA]

Funkci create_async použijte pouze, když je nutné vytvořit funkcionalitu, která je přístupná z jiného jazyka nebo jiné komponenty Windows Runtime.Třídu task použijte přímo, pokud víte, že operace je vyrobena i spotřebována v kódu jazyka C++ ve stejné komponentě.

Návratový typ create_async je určen typem jeho argumentů.Například pokud pracovní funkce nevrací hodnotu a nevypisuje průběh, funkce create_async vrátí IAsyncAction.Pokud pracovní funkce nevrací hodnotu, ale výpis průběhu, funkce create_async vrátí IAsyncActionWithProgress.Pro výpisy průběhu je nutné pracovní funkci předat jako parametr objekt concurrency::progress_reporter.Možnost hlášení průběhu umožňuje oznámit, jaké množství práce bylo provedeno a jaké množství stále zůstává (například v procentech).Také lze získat výsledky sestavy, jakmile budou k dispozici.

Každé rozhraní IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> a IAsyncActionOperationWithProgress<TProgress, TProgress> poskytuje metodu Cancel, která umožňuje zrušit asynchronní operaci.Třída task spolupracuje s tokeny zrušení.Použijete-li ke zrušení práce token zrušení, modul runtime nespustí novou práci, která se k tomuto tokenu přihlásí.Práce, která je již aktivní, může sledovat svůj token zrušení a zastavit, je-li to možné.Tento mechanismus je podrobněji popsán v dokumentu Zrušení v PPL.Zrušení úlohy lze k metodám Cancel Windows Runtime připojit dvěma způsoby.Za prvé, lze definovat pracovní funkce, které budou předány funkci create_async, aby získaly objekt concurrency::cancellation_token.Při zavolání metody Cancel je tento token zrušení zrušen a jsou aplikována normální pravidla zrušení pro podkladový objekt task, který podporuje volání create_async.Jestliže není objekt cancellation_token poskytnut, podkladový objekt task si implicitně jeden vytvoří.Jestliže je potřeba kooperativně reagovat na zrušení ve funkci práce, definujte objekt cancellation_token.V části Příklad: Řízení běhu v aplikaci Windows Store v jazyce C++ a XAML je uveden příklad jak provést zrušení v Windows Store aplikaci v jazyce C# a XAML, která používá vlastní C++ komponentu Windows Runtime.

Poznámka k upozorněníUpozornění

V řetězci pokračování úkolů vždy vyčistěte stav a poté zavolejte concurrency::cancel_current_task, když funkce concurrency::is_task_cancellation_requested vrátí true.Pokud se namísto volání metody cancel_current_task provede předčasný návrat, operace přejde do stavu dokončeno namísto zrušeno.

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

Pro vytvoření rozhraní Windows Runtime

Vraťte tento typ z funkce create_async

Tyto typy parametrů předejte funkci práce, aby byl použít implicitní token zrušení

Tyto typy parametrů předejte funkci práce, aby byl použít explicitní token 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)

Z funkce práce lze vrátit hodnotu nebo objekt task, který předáte funkci create_async.Tyto variace způsobí jiné chování.Po vrácení hodnoty je funkce práce zabalena do objektu task tak, aby ji bylo možné spustit ve vlákně na pozadí.Kromě toho, podkladový objekt task používá implicitní token zrušení.Naopak, pokud bude vrácen objekt task, bude pracovní funkce pracovat synchronně.Proto pokud bude vrácen objekt task, je třeba se ujistit, že jakákoli dlouhotrvající operace v pracovní funkci také běží jako úkol, aby aplikace neustále reagovala.Kromě toho, podkladový objekt task nepoužívá implicitní token zrušení.Proto je třeba definovat, aby pracovní funkce přijímala objekt cancellation_token, pokud je vyžadována podpora pro zrušení po vracení objektu task z funkce create_async.

Následující příklad ukazuje různé způsoby vytváření objektu IAsyncAction, který může být používán jinou komponentou 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.
    });
});

[Nahoru]

Příklad: Vytvoření komponenty Windows Runtime jazyka C++ a její použití v jazyce C#

Představte si aplikaci, která používá XAML a jazyk C# pro definici uživatelského rozhraní a komponentu jazyka C++ Windows Runtime provádějící náročné výpočetní operace.V tomto příkladu určí komponenta jazyka C++, která čísla v daném rozsahu jsou prvočísla.K ilustraci rozdílů mezi čtyřmi asynchronními rozhraními úkolů Windows Runtime, spusťte aplikaci Visual Studio a vytvořte Čisté řešení a pojmenujte jej Primes.Poté do řešení přidejte projekt Součást prostředí Windows Runtime a pojmenujte jej PrimesLibrary.Do vygenerovaného souboru hlaviček C++ (v tomto příkladu přejmenujeme Class1.h na Primes.h) přidejte následující kód.Každá metoda s přístupem public definuje jedno ze čtyř asynchronních rozhraní.Metody vracející hodnotu vrací objekt Windows::Foundation::Collections::IVector<int>.Metody, které vykazují průběh produkují hodnoty double, 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ázvů jsou názvy asynchronních metod v Windows Runtime zpravidla zakončeny "Async".

Do vygenerovaného zdrojového souboru C++ (v tomto příkladu přejmenujeme Class1.cpp na Primes.cpp) přidejte následující kód .Funkce is_prime určuje, zda je jeho vstupem prvočíslo.Zbývající metody implementují třídu Primes.Každé volání funkce create_async používá podpis, který je kompatibilní s metodou, ze které je volána.Například protože Primes::ComputePrimesAsync vrací IAsyncAction, pracovní funkce, která je poskytnuta funkci create_async, nevrací hodnotu a nepřijímá v parametru objekt progress_reporter.

// 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í k zajištění toho, aby byly vstupní parametry kladné.Pokud je vstupní hodnota záporná, vyvolá metoda výjimku Platform::InvalidArgumentException.Zpracování chyb je vysvětleno dále v této části.

Využití těchto metod z Windows Store aplikací, použijte pro přidání druhého projektu řešení systému Visual Studio šablonu Prázdná aplikace jazyka Visual C# (XAML).V tomto příkladu pojmenujte projektu Primes.Pak do projektu Primes přidejte odkaz na projekt PrimesLibrary.

Přidejte následující kód do souboru MainPage.xaml.Tento kód definuje uživatelské rozhraní, takže lze volat komponentu jazyka C++ a zobrazit výsledky.

<Page
    x:Class="Primes.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Primes"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://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 MainPage v souboru MainPage.xaml přidejte následující kód.Tento kód definuje objekt Primes a obslužné rutiny události 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í klíčová slova async a await pro aktualizaci uživatelského rozhraní, po dokončení asynchronní operace.Informace o asynchronních vzorech, které jsou k dispozici v jazycích C# a Visual Basic naleznete v tématech týkajících se asynchronních vzorů ve Windows Store aplikacích v jazyce C# a asynchronních vzorů ve Windows Store aplikacích v jazyce Visual Basic.

Metody getPrimesCancellation a cancelGetPrimes pracují společně, aby umožnily uživateli zrušit operaci.Když uživatel klikne na tlačítko Zrušit, metoda cancelGetPrimes zavolá IAsyncOperationWithProgress<TResult, TProgress>::Cancel pro zrušení operace.Modul Concurrency Runtime, který spravuje podkladové asynchronní operace, vyvolá vnitřní výjimku, která je zachycena Windows Runtime, říká tak, že zrušení bylo dokončeno.Další informace o modelu zrušení naleznete v tématu Zrušení v PPL.

Důležitá poznámkaDůležité

Chcete-li povolit, aby modul Concurrency Runtime správně reportoval do Windows Runtime, že zrušil operaci, nezachycujte tento typ vnitřní výjimky.To znamená, že by také neměly být zachyceny všechny výjimky (catch (...)).Je-li nutné zachytit všechny výjimky, vyvolejte výjimku znovu, aby bylo zajištěno, že Windows Runtime dokončí operaci zrušení.

Následující ilustrace ukazuje aplikaci Primes aplikace poté, co byly vybrány všechny možnosti.

Úložiště Windows Primes app

Příklady, které používají create_async k vytvoření asynchronních úloh, které mohou být zpracovány jinými jazyky, naleznete v tématu týkajícím se použití jazyka C++ v ukázce optimalizace cest v mapách Bing nebo asynchronní operace v operačním systému Windows 8 v jazyce C++ s knihovnou PPL.

[Nahoru]

Řízení vlákna provádění

Windows Runtime používá vláknový model COM.V tomto modelu jsou objekty hostovány v různých apartment objektech v závislosti na tom, jak zpracovávají své synchronizace.Objekty bezpečné pro přístup z více vláken jsou hostovány ve vícevláknovém objektu apartment (MTA).Objekty, ke kterým musí být přistupováno jedním vláknem, jsou umístěny v jednovláknovém objektu apartment (STA).

V aplikaci, která má uživatelské rozhraní, je vlákno ASTA (Application STA) zodpovědné za okno zprávy a je jediným vláknem v procesu, které může aktualizovat ovládací prvky uživatelského rozhraní hostované v STA.To má dva důsledky.Za prvé, aby aplikace nadále reagovala optimálně, všechny vstupně-výstupní operace a operace náročné na CPU by neměly být spuštěny ve vlákně ASTA.Za druhé, výsledky, které pocházejí z vláken na pozadí musí být pro aktualizaci uživatelského rozhraní zařazeny zpět do vlákna ASTA.Ve Windows Store aplikaci v jazyce C++, MainPage a další XAML stránky běží ve vlákně ASTA.Proto pokračování úlohy, která jsou deklarována v ASTA, pracují ve výchozím nastavení tak, aby bylo možné je aktualizovat přímo v těle pokračování.Pokud ale je úkol vnořen do jiného úkolu, jakékoli pokračování v tomto vnořeném úkolu běží v MTA.Proto je třeba zvážit, zda má být explicitně specifikováno, v jakém kontextu mají být tato pokračování spuštěna.

Úkol, který je vytvořen ze asynchronní operace, jako například IAsyncOperation<TResult>, používá speciální sémantiku, která může pomoci ignorovat podrobnosti práce s vlákny.Ačkoli operace může být spuštěna ve vlákně na pozadí (nebo nemusí být spuštěna ve vlákně vůbec), ve výchozím nastavení je zaručeno, že její pokračování budou běžet v objektu apartment, který spustil operace pokračování (jinými slovy z objektu apartment, který volal task::then).Pro řízení kontextu provádění pokračování lze použít třídu concurrency::task_continuation_context.K vytvoření objektů task_continuation_context použijte následující statické pomocné metody:

Metodě task::then lze předat objekt task_continuation_context pro explicitní správu nad kontextem provádění pokračování, nebo také lze předat úkol jinému objektu apartment a následně zavolat metodu task::then pro implicitní řízení kontextu provádění.

Důležitá poznámkaDůležité

Protože je hlavní vlákno uživatelského rozhraní Windows Store aplikací spouštěno v STA, pokračování, která vytvoříte v tomto STA, budou ve výchozím nastavení spuštěna v STA.V důsledku toho pokračování, která budou vytvořena v MTA, budou v MTA spuštěna.

Následující část popisuje aplikaci, která čte soubor z disku, najde většinu běžných slov v tomto souboru a poté zobrazí výsledky v uživatelském rozhraní.K poslední operaci (aktualizace uživatelského rozhraní), dochází ve vlákně uživatelského rozhraní.

Důležitá poznámkaDůležité

Toto chování je specifické pro aplikace Windows Store.U aplikací klasické pracovní plochy nelze určit, kde se má pokračovat.Místo toho plánovač zvolí pracovní vlákno, na kterém budou běžet všechna pokračování.

Důležitá poznámkaDůležité

Nedoporučuje se volat metodu concurrency::task::wait v těle pokračování, které běží v STA.Jinak modul runtime vyvolá výjimku concurrency::invalid_operation, jelikož tato metoda blokuje aktuální vlákno a může způsobit, že aplikace přestane odpovídat.Nicméně lze pro získání výsledku předchozího úkolu v pokračování založeném na úkolech zavolat metodu concurrency::task::get.

[Nahoru]

Příklad: Řízení provádění ve Windows Store aplikaci v jazyce C++ a XAML

Představte si aplikaci C++ XAML, která načte soubor z disku, vyhledá v něm nejběžnější slova a následně ukáže výsledky v uživatelském rozhraní.Pro vytvoření této aplikace spusťte systém Visual Studio a vytvořte projekt Windows Store Prázdná aplikace (XAML) a pojmenujte jej CommonWords.Do manifestu aplikace zadejte možnost Knihovna dokumentů, aby byl aplikaci povolen přístup ke složce Dokumenty.Dále přidejte do oddílu deklarací manifestu aplikace soubor typu Text (.txt).Další informace o možnostech aplikace a deklaracích naleznete v tématu týkajícím se balíčků a nasazení aplikací.

Aktualizujte element Grid v souboru MainPage.xaml, aby obsahoval elementy ProgressRing a TextBlock.Element ProgressRing označuje, že operace probíhá a element TextBlock zobrazuje výsledky výpočtu.

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

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

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

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

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í příkazy using.

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

V souboru MainPage.cpp, implementujte metody MainPage::MakeWordList, MainPage::FindCommonWords a MainPage::ShowResults.Metody MainPage::MakeWordList a MainPage::FindCommonWords provádějí výpočetně náročné operace.Metoda MainPage::ShowResults zobrazuje 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());
}

Upravte konstruktor MainPage tak, aby vytvořil řetěz pokračování úkolů, které zobrazují nejběžnější slova z knihy Ilias od Homéra.První dva úkoly pokračování, které rozdělily text do jednotlivých slov a vyhledaly nejběžnější slova, mohou být časově náročné, a jsou proto explicitně nastaveny tak, aby běžely na pozadí.Konečný úkol pokračování, který aktualizuje uživatelské rozhraní, neurčuje žádný kontext pokračování a proto se řídí pravidly vláknového modelu apartment.

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 spuštění kontextů a jak vytvořit řetězec pokračování.Vzpomeňte si, že ve výchozím nastavení úkol, který je vytvořen z asynchronní operace, spustí své pokračování v objektu apartment, který zavolal metodu task::then.Proto tento příklad používá k určení těchto operací, které nezahrnují provádění uživatelského rozhraní ve vlákně na pozadí, metodu task_continuation_context::use_arbitrary.

Následující ilustrace ukazuje výsledky aplikace CommonWords.

Windows Store CommonWords app

V tomto příkladu je možné podporovat zrušení, protože objekty task, které podporují metodu create_async, používají implicitní token zrušení.Definujte pracovní funkci tak, aby přijímala objekt cancellation_token, pokud úkol vyžaduje odpověď na zrušení ve formě spolupráce.Další informace o zrušení v knihovně PPL naleznete v tématu Zrušení v PPL

[Nahoru]

Viz také

Koncepty

Souběžnost Runtime