Création d'opérations asynchrones en C++ pour les applications Windows Store
Ce document décrit certains points clés pour tenir compte lorsque vous utilisez le runtime d'accès concurrentiel pour produire des opérations asynchrones dans une application d' Windows Store .
L'utilisation de la programmation asynchrone est un composant clé dans le modèle d'application d' Windows Store car elle permet aux applications de rester sensibles à l'entrée d'utilisateur.Vous pouvez lancer une tâche de longue durée sans bloquer le thread d'interface utilisateur, et vous pouvez recevoir les résultats de la tâche ultérieurement.Vous pouvez également annuler des tâches et recevoir des notifications de progression que les tâches exécutant en arrière-plan.Le document Programmation asynchrone en C++ fournit une vue d'ensemble du modèle asynchrone qui est disponible dans Visual C++ pour créer des applications d' Windows Store .Ce document explique comment valeur consommez et créer des chaînes des opérations asynchrones d' Windows Runtime .Cette section décrit comment utiliser le runtime d'accès concurrentiel pour produire des opérations asynchrones qui peuvent être consommées par un autre composant d' Windows Runtime et comment contrôler comment le travail asynchrone est exécuté.Envisagez également de le Modèles et conseils de programmation Async dans Hilo (les fenêtres stockent des applications à l'aide de C++ et le code XAML) lire pour savoir comment nous avons utilisé le runtime d'accès concurrentiel pour implémenter les opérations asynchrones dans Hilo, une application Windows Store l'aide de C++ et le XAML.
[!REMARQUE]
Vous pouvez utiliser Bibliothèque de modèles parallèles (PPL) et Bibliothèque d'agents asynchrones dans une application d' Windows Store .Toutefois, vous ne pouvez pas utiliser le planificateur de tâches ou le gestionnaire de ressources.Ce document décrit les fonctionnalités supplémentaires que le runtime d'accès concurrentiel fournit disponibles uniquement à une application d' Windows Store, et non à une application de bureau.
Points clés
Utilisez concurrency::create_async pour créer des opérations asynchrones qui peuvent être utilisées par d'autres composants (qui peuvent être écrits dans d'autres langages que C++).
Utilisez concurrency::progress_reporter pour stocker des notifications de progression aux composants qui appellent vos opérations asynchrones.
Utilisez les jetons d'annulation pour permettre aux opérations asynchrones internes d'annuler.
Le comportement de la fonction d' create_async dépend du type de retour de la fonction de travail qui est passée à celui-ci.Une fonction de travail qui retourne les opérations d'une tâche ( task<T> ou task<void>) de façon synchrone dans le contexte qui a appelé create_async.Une fonction de travail qui retourne T ou void s'exécute dans un contexte arbitraire.
Vous pouvez utiliser la méthode d' concurrency::task::then pour créer une chaîne des tâches qui utilisent l'un après l'autre.Dans une application d' Windows Store, le contexte par défaut pour les suites d'une tâche dépend de la manière dont cette tâche a été générée.Si la tâche a été créée en passant une action asynchrone au constructeur de tâche, ou en passant une expression lambda qui retourne une action asynchrone, le contexte par défaut pour toutes les suites de cette tâche est le contexte actuel.Si la tâche n'est pas développée d'une action asynchrone, un contexte arbitraire est utilisé par défaut pour les suites de la tâche.Vous pouvez substituer le contexte par défaut à la classe d' concurrency::task_continuation_context .
Dans ce document :
Créer des opérations asynchrones
Exemple : Créer le composant de runtime windows c++
Contrôle du thread d'exécution
Exemple : Exécution de contrôle dans une mémoire Applications windows avec C++ et le XAML
Créer des opérations asynchrones
Vous pouvez utiliser la tâche et le modèle de suite à la bibliothèque de modèles parallèles (PPL) de définir des tâches en arrière-plan ainsi que les tâches supplémentaires qui s'exécutent lorsque la tâche précédente se termine.Cette fonctionnalité est fournie par la classe d' concurrency::task .Pour plus d'informations sur ce modèle et la classe d' task, consultez Parallélisme des tâches (runtime d'accès concurrentiel).
Windows Runtime est une interface de programmation que vous pouvez utiliser pour créer des applications d' Windows Store qui fonctionnent uniquement dans un environnement du système d'exploitation spécial.Une telle utilisation d'applications autorisée s'exécute, les types de données, et des appareils, et est distribuée d' Windows Store.Windows Runtime est représenté par l'interface binaire d'application (ABI).ABI est un contrat binaire sous-jacent qui rend les API d' Windows Runtime disponibles pour les langages de programmation tels que Visual C++.
À l'aide de Windows Runtime, vous pouvez utiliser les meilleures fonctionnalités des différents langages de programmation et les combiner dans une application.Par exemple, vous pouvez créer votre interface utilisateur dans JavaScript et exécuter la logique nécessitant de nombreuses ressources de calcul d'application dans le composant c++.La possibilité d'effectuer ces opérations nécessitant de nombreuses ressources de calcul est en arrière-plan un facteur clé en conservant votre interface utilisateur reste réactive.Étant donné que la classe d' task est spécifique à C++, vous devez utiliser une interface d' Windows Runtime pour communiquer des opérations asynchrones à d'autres composants (qui peuvent être écrits dans d'autres langages que C++).Windows Runtime fournit quatre interfaces que vous pouvez utiliser pour représenter des opérations asynchrones :
Windows::Foundation::IAsyncAction
Représente une action asynchrone.Windows::Foundation::IAsyncActionWithProgress<TProgress>
Représente une action asynchrone que les rapports progression.Windows::Foundation::IAsyncOperation<TResult>
Représente une opération asynchrone qui retourne un résultat.Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Représente une opération asynchrone qui retourne un résultat et les rapports progression.
La notion d'une action signifie que la tâche asynchrone ne produit pas une valeur (pensez à une fonction qui retourne void).La notion d'une opération signifie que la tâche asynchrone produit une valeur.La notion de la progression signifie que la tâche peut stocker des messages de progression à l'appelant.JavaScript, le.NET Framework, et Visual C++ et utilise sa propre façon de créer des instances de ces interfaces pour l'utiliser à travers la limite d'ABI.Pour Visual C++, le runtime d'accès concurrentiel fournit la fonction d' concurrency::create_async .Cette fonction crée une action ou une opération asynchrone d' Windows Runtime qui représentent l'achèvement d'une tâche.La fonction d' create_async prend une fonction de travail (généralement une expression lambda), crée en interne un objet d' task, et les encapsule qui tâche dans une des quatre interfaces asynchrones d' Windows Runtime .
[!REMARQUE]
Utilisez create_async uniquement lorsque vous devez créer la fonctionnalité qui est accessible à partir d'un autre langage ou d'un composant différent d' Windows Runtime .Utilisez la classe d' task directement lorsque vous savez que l'opération est produite et consommée par du code C++ dans le même composant.
Le type de retour d' create_async est déterminé par le type de ses arguments.Par exemple, si votre fonction de travail ne retourne pas de valeur et ne stocke pas la progression, create_async retourne IAsyncAction.Si la fonction de travail ne retourne pas de valeur ainsi que les rapports progression, create_async retourne IAsyncActionWithProgress.Pour signaler la progression, fournissez un objet d' concurrency::progress_reporter comme paramètre à la fonction de travail.La possibilité de signaler la progression vous permet de stocker quelle quantité de travail a été exécutée et la proportion reste toujours (par exemple, en pourcentage).Elle vous permet également de stocker les résultats à mesure qu'elles deviennent disponibles.
IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, et les interfaces chacune d' IAsyncActionOperationWithProgress<TProgress, TProgress> fournissent une méthode d' Cancel qui vous permet d'annuler l'opération asynchrone.La classe d' task fonctionne avec les jetons d'annulation.Lorsque vous utilisez un jeton d'annulation pour annuler du travail, le runtime ne démarre pas de travail qui s'abonnent à ce jeton.Fonctionnent déjà active peut contrôler son jeton d'annulation et arrêter lorsqu'il peut.Ce mécanisme est décrit plus en détail dans le document Annulation dans la bibliothèque de modèles parallèles.Vous pouvez connecter l'annulation de tâche aux méthodes d' Windows RuntimeCancel de deux façons.D'abord, vous pouvez définir la fonction de travail que vous passez à create_async prendre un objet d' concurrency::cancellation_token .Lorsque la méthode d' Cancel est appelée, ce jeton d'annulation est annulé et les règles normales d'annulation appliquent à task l'objet sous-jacent qui prend en charge l'appel d' create_async .Si vous ne fournissez pas l'objet d' cancellation_token, l'objet sous-jacent d' task définit un implicitement.Définissez un objet d' cancellation_token lorsque vous devez répondre à l'annulation coopérative dans la fonction de travail.La section Exemple : Exécution de contrôle dans une mémoire Applications windows avec C++ et le XAML illustre un exemple d'effectuer l'annulation dans une application d' Windows Store avec le c et le code XAML qui utilise un composant personnalisé d' Windows Runtime C++.
Attention |
---|
Dans une chaîne les continuations de tâches, nettoyer toujours l'état et appelez ensuite concurrency::cancel_current_task lorsque concurrency::is_task_cancellation_requested retourne true.Si vous retournez haut au lieu d'appeler cancel_current_task, les transitions d'exécution à l'état terminé au lieu de l'état d'annulation. |
Le tableau suivant résume les combinaisons que vous pouvez utiliser pour définir des opérations asynchrones dans votre application.
Pour créer cette interface d' Windows Runtime |
Retournez ce type d' create_async |
Passez ces types de paramètre à la fonction de travail pour utiliser un jeton d'annulation implicite |
Passez ces types de paramètre à la fonction de travail pour utiliser un jeton d'annulation explicite |
---|---|---|---|
IAsyncAction |
void ou task<void> |
(aucun) |
(cancellation_token) |
IAsyncActionWithProgress<TProgress> |
void ou task<void> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
IAsyncOperation<TResult> |
T ou task<T> |
(aucun) |
(cancellation_token) |
IAsyncActionOperationWithProgress<TProgress, TProgress> |
T ou task<T> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
Vous pouvez retourner une valeur ou un objet d' task de la fonction de travail que vous passez à la fonction d' create_async .Ces variations produisent des comportements.Lorsque vous retournez une valeur, la fonction de travail est encapsulée dans task afin qu'elle puisse être exécutée sur un thread d'arrière-plan.En outre, task sous-jacent utilise un jeton implicite d'annulation.Inversement, si vous retournez un objet d' task, la fonction de travail fonctionne de façon synchrone.Par conséquent, si vous retournez un objet d' task, assurez-vous que toutes les opérations longues dans la fonction de travail s'exécutent également en tant que tâches de permettre à votre application de rester sensible.En outre, task sous-jacent n'utilise pas un jeton implicite d'annulation.Par conséquent, vous devez définir votre fonction de travail pour prendre un objet d' cancellation_token si vous avez besoin de la prise en charge de l'annulation lorsque vous retournez un objet d' task d' create_async.
L'exemple suivant affiche les différentes façons de créer un objet d' IAsyncAction qui peut être consommé par un autre composant d' 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.
});
});
[Supérieur]
Exemple : Création du composant et le consommer d'exécution de fenêtres c++ du C
Considérez une application qui utilise XAML et c pour définir le composant d'interface utilisateur et de c++ Windows Runtime pour exécuter des opérations calcul- intensives.Dans cet exemple, les calculs de composant C++ qui compte dans un intervalle donné sont principaux.Pour illustrer les différences entre les quatre interfaces asynchrones du travail d' Windows Runtime, démarrez, dans Visual Studio, créez Nouvelle solution et en nommant Amorce.Ajoutez ensuite à la solution un projet de Composant Windows Runtime et le nom PrimesLibrary.Ajoutez le code suivant au fichier d'en-tête généré C++ (cet exemple renomme Class1.h à Primes.h).Chaque méthode d' public définit une des quatre interfaces asynchrones.Les méthodes qui retournent un retour Windows::Foundation::Collections::IVector<int> de valeur un objet.Les méthodes qui enregistrent les valeurs d' double de produit de progression qui définissent le pourcentage de travail global qui est terminé.
#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);
};
}
[!REMARQUE]
Par convention, les noms de méthodes asynchrones dans Windows Runtime terminent généralement par « Async ».
Ajoutez le code suivant au fichier source généré C++ (cet exemple renomme Class1.cpp à Primes.cpp).La fonction d' is_prime détermine si son entrée est principale.Les méthodes restantes implémentent la classe d' Primes .Chaque appel à create_async utilise une signature compatible avec la méthode de laquelle elle est appelée.Par exemple, parce qu' Primes::ComputePrimesAsync retourne IAsyncAction, la fonction de travail fournie à create_async ne retourne pas de valeur et ne prend pas d'objet d' progress_reporter comme paramètre.
// 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;
});
}
Chaque méthode exécute d'abord la validation pour vérifier que les paramètres d'entrée sont négatifs.Si une valeur d'entrée est négative, la méthode lève Platform::InvalidArgumentException.La gestion des erreurs est illustrée plus loin dans cette section.
Pour utiliser ces méthodes d'une application d' Windows Store, utilisez le modèle Visual C# Application vide (XAML) pour ajouter un deuxième projet à la solution Visual Studio.Cet exemple nomme le projet Amorce.Ensuite, le projet d' Amorce, ajoutez une référence au projet d' PrimesLibrary .
Ajoutez le code suivant à MainPage.xaml.Ce code définit l'interface utilisateur afin que vous puissiez appeler le composant C++ et afficher les résultats.
<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>
Ajoutez le code suivant à la classe d' MainPage MainPage.xaml dans.Ce code définit un objet d' Primes et les gestionnaires d'événements de bouton.
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();
}
Ces méthodes utilisent des mots clés d' async et d' await pour mettre à jour l'interface utilisateur après les opérations asynchrones complètes.Pour plus d'informations sur les modèles asynchrones qui sont disponibles dans c et en Visual Basic, consultez Les modèles asynchrones dans les fenêtres stockent des applications avec le C et Les modèles asynchrones dans les fenêtres stockent des applications avec VB.
Les méthodes d' getPrimesCancellation et d' cancelGetPrimes fonctionnent ensemble pour permettre à l'utilisateur d'annuler l'opération.Lorsque l'utilisateur choisit le bouton Annuler, les appels de méthode IAsyncOperationWithProgress<TResult, TProgress>::Cancel d' cancelGetPrimes pour annuler l'opération.Le runtime d'accès concurrentiel, qui gère l'opération asynchrone sous-jacente, lève un type d'exception interne qui est interceptée par Windows Runtime pour communiquer que l'annulation est terminée.Pour plus d'informations sur le modèle d'annulation, consultez Annulation dans la bibliothèque de modèles parallèles.
Important
Pour permettre au runtime d'accès concurrentiel de signaler correctement à Windows Runtime qu'il a annulé l'opération, n'interceptez pas ce type d'exception interne.Cela signifie que vous ne devez pas intercepter les exceptions (catch (...)).Si vous devez intercepter toute exception, l'exception générale à s'assurer qu' Windows Runtime peut effectuer l'opération d'annulation.
L'illustration suivante montre l'application d' Amorce après chaque option a été sélectionné.
Pour obtenir des exemples qui utilisent create_async pour créer des tâches asynchrones qui peuvent être consommées par d'autres langages, consultez À l'aide de C++ dans le Bing Maps déclenchez-vous l'exemple de l'optimiseur et Windows 8 opérations asynchrones en C++ avec modèles parallèles.
[Supérieur]
Contrôle du thread d'exécution
Windows Runtime utilise le modèle de thread COM.Dans ce modèle, les objets sont hébergés dans des apartments, selon la façon dont ils gèrent leur synchronisation.Les objets thread-safe sont hébergés dans multithread apartment (MTA).Les objets qui doivent être accessibles par un thread unique sont hébergés dans un thread apartment (STA).
Dans une application qui a une interface utilisateur, le thread d'ASTA application (STA) est chargé de pomper des messages de fenêtre et est le seul thread dans le processus qui peut mettre à jour les contrôles hébergés STA- d'interface utilisateur.Cela a deux conséquences.D'abord, pour permettre à l'application de rester sensible, toutes les gourmand en ressources processeur et opérations d'E/S ne doivent pas être exécutés sur le thread d'ASTA.Ensuite, les résultats provenant des threads d'arrière-plan doit être marshalé vers l'ASTA pour mettre à jour l'interface utilisateur.Dans l'application c++ Windows Store, MainPage et tout autre code XAML pages tous exécutés sur l'ATSA.Par conséquent, les continuations de tâches qui sont déclarées sur l'ASTA sont exécutées là par défaut afin que vous puissiez mettre à jour des contrôles directement au corps de suite.Toutefois, si vous emboîtez une tâche dans une autre tâche, toutes les suites sur celle qui est imbriqué la tâche exécutée dans le MTA.Par conséquent, vous devez déterminer si spécifier explicitement sur le contexte exécution de ces suites.
Une tâche créée par une opération asynchrone, telle qu' IAsyncOperation<TResult>, la sémantique d'utilisation spéciale qui peut vous aider à ignorer les threads en détail.Bien qu'une opération peut s'exécuter sur un thread d'arrière-plan (ou ne peut pas être stocké par un thread du tout), ses continuations par défaut est garantie s'exécuter sur le MTA qui a démarré les opérations de continuation (en d'autres termes, l'apartment (cloisonné) qui task::thenappelé).Vous pouvez utiliser la classe d' concurrency::task_continuation_context pour contrôler le contexte d'exécution d'une suite.Utilisez ces méthodes d'assistance statiques pour créer des objets d' task_continuation_context :
Utilisation concurrency::task_continuation_context::use_arbitrary de spécifier que la continuation s'exécute sur un thread d'arrière-plan.
Utilisation concurrency::task_continuation_context::use_current de spécifier que la continuation s'exécute sur le thread qui a appelé task::then.
Vous pouvez passer un objet d' task_continuation_context à la méthode d' task::then pour contrôler explicitement le contexte d'exécution de la suite ou vous pouvez passer la tâche à un autre apartment (cloisonné) et appeler la méthode d' task::then pour contrôler implicitement le contexte d'exécution.
Important
Étant donné que le thread d'interface utilisateur principal d' Windows Store exécutant sur STA, les suites que vous créez dans celui STA par défaut s'exécutent sur le STA.Par conséquent, les suites que vous créez dans le MTA s'exécutent sur le MTA.
La section suivante montre une application qui lit un fichier à partir de le disque, recherche les mots les plus courants dans ce fichier, puis affiche les résultats dans l'interface utilisateur.L'exécution finale, mise à jour de l'interface utilisateur, se produit sur le thread d'interface utilisateur.
Important
Ce comportement est spécifique aux applications d' Windows Store .Pour les applications de bureau, vous ne vérifiez pas si l'exécution de suites.À la place, le planificateur choisit un thread de travail sur lequel exécuter chaque suite.
Important
N'appelez pas concurrency::task::wait dans le corps d'une continuation qui s'exécute sur le STA.Sinon, le runtime lève concurrency::invalid_operation car cette méthode bloque le thread actuel et peut entraîner l'application répond pas.Toutefois, vous pouvez appeler la méthode d' concurrency::task::get pour accepter le résultat de l'antécédent dans une suite tâche- sur.
[Supérieur]
Exemple : Exécution de contrôle dans Windows Store Applications avec C++ et le XAML
Considérez l'application c++ XAML qui lit un fichier à partir de le disque, recherche les mots les plus courants dans ce fichier, puis affiche les résultats dans l'interface utilisateur.Pour créer cette application, démarrez, dans Visual Studio, créez un projet d' Windows StoreApplication vide (XAML) et en nommant CommonWords.Dans votre manifeste d'application, spécifiez la capacité Bibliothèque de documents pour permettre à l'application d'accéder au dossier Documents.Ajoutez également le type de fichier texte (.txt) à la section des déclarations du manifeste d'application.Pour plus d'informations sur les fonctions et les déclarations d'application, consultez Modules et déploiement d'Applications.
Mettez à jour l'élément de Grid MainPage.xaml dans pour inclure un élément d' ProgressRing et un élément d' TextBlock .ProgressRing indique que l'opération est en cours et l'échelle d' TextBlock le résultat du calcul.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ProgressRing x:Name="Progress"/>
<TextBlock x:Name="Results" FontSize="16"/>
</Grid>
Ajoutez les instructions suivantes d' #include à pch.h.
#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>
Ajoutez les déclarations de méthode suivante à la classe d' MainPage (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);
Ajoutez les instructions suivantes d' using à MainPage.cpp.
using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
Dans MainPage.cpp, appliquez MainPage::MakeWordList, MainPage::FindCommonWords, et les méthodes d' MainPage::ShowResults .MainPage::MakeWordList et MainPage::FindCommonWords exécutent des opérations nécessitant de nombreuses ressources de calcul.La méthode d' MainPage::ShowResults affiche le résultat du calcul dans l'interface utilisateur.
// 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());
}
Modifiez le constructeur d' MainPage pour créer une chaîne des tâches de continuation qui affiche dans l'interface utilisateur que le communs exprime dans l'ouvrage l'iliade par Homer.Les deux premières tâches de continuation, qui coupent le texte en mots individuels et recherchez des mots communs, peuvent être coûteuses en temps et par conséquent sont explicitement définies pour exécuter en arrière-plan.La tâche de continuation finale, qui met à jour l'interface utilisateur, ne spécifie aucun contexte de suite, et par conséquent suit les règles de thread cloisonné.
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.
});
}
[!REMARQUE]
Cet exemple montre comment spécifier des contextes d'exécution et comment composer une chaîne des suites.Appelez de nouveau à celui par défaut une tâche créée par une opération asynchrone exécute ses continuations dans le MTA qui a appelé task::then.Par conséquent, cet exemple utilise task_continuation_context::use_arbitrary pour spécifier que les opérations qui n'impliquent pas l'interface utilisateur soient exécutées sur un thread d'arrière-plan.
L'illustration suivante montre les résultats de l'application d' CommonWords .
Dans cet exemple, il est possible de prendre en charge l'annulation car les objets d' task qui prennent en charge l'utilisation d' create_async un jeton implicite d'annulation.Définissez la fonction de travail pour prendre un objet d' cancellation_token si vos tâches doivent répondre à l'annulation de manière coopérative.Pour plus d'informations sur l'annulation dans la bibliothèque PPL, consultez Annulation dans la bibliothèque de modèles parallèles
[Supérieur]