Crear operaciones asincrónicas en C++ para aplicaciones de la Tienda Windows
Este documento se describen algunos de los puntos clave para tener en cuenta cuando se usa el runtime de simultaneidad para generar operaciones asincrónicas en una aplicación de Tienda Windows.
El uso de la programación asincrónica es un componente clave en el modelo de aplicaciones de Tienda Windows porque permite a aplicaciones siga respondiendo a los datos proporcionados por el usuario.Puede iniciar una tarea de ejecución prolongada sin bloquear el subproceso de la interfaz de usuario, y puede recibir los resultados de la tarea más adelante.También puede cancelar tareas y recibir notificaciones de progreso como tareas ejecutadas en segundo plano.El documento Programación asincrónica en C++ se proporciona información general sobre el modelo asincrónico que está disponible en Visual C++ crear aplicaciones de Tienda Windows.Ese documento enseña cómo a consumir y crear las cadenas de operaciones asincrónicas de Windows en tiempo de ejecución.En esta sección se describe cómo usar el runtime de simultaneidad para generar las operaciones asincrónicas que pueden usar por otro componente de Windows en tiempo de ejecución y cómo controlar cómo se ejecuta el trabajo asincrónico.También puede leer Modelos y sugerencias de programación Async en Subproceso (Windows almacena aplicaciones mediante C++ y XAML) para obtener información sobre cómo se usa el runtime de simultaneidad para implementar operaciones asincrónicas de Subproceso, una aplicación de Tienda Windows mediante C++ y XAML.
[!NOTA]
Puede utilizar Paralela a la biblioteca de modelos (PPL) y Biblioteca de agentes asincrónicos en una aplicación de Tienda Windows.Sin embargo, no puede utilizar el Programador de tareas o el administrador de recursos.Este documento describe características adicionales que el runtime de simultaneidad proporciona que solo está disponible para una aplicación de Tienda Windows, no a una aplicación de escritorio.
Puntos clave
Utilice concurrency::create_async para crear las operaciones asincrónicas que otros componentes pueden utilizar (que pueden estar escritos en otros lenguajes distintos de C++).
Uso concurrency::progress_reporter de notificar notificaciones de progreso componentes que llama a las operaciones asincrónicas.
Utilice los tokens de cancelación para permitir a operaciones asincrónicas internas para cancelar.
El comportamiento de la función de create_async depende del tipo de valor devuelto de la función de trabajo que se le pasa.Una función de trabajo que devuelve se ejecuta una tarea ( task<T> o task<void>) sincrónicamente en el contexto que llamó a create_async.Una función de trabajo que devuelve T o void se ejecuta en un contexto arbitrario.
Puede utilizar el método de concurrency::task::then para crear una cadena de las tareas que se ejecutan uno tras otro.En una aplicación de Tienda Windows, el contexto predeterminado para las continuaciones de una tarea depende de cómo esa tarea se construyó.Si la tarea se realiza pasando una acción asincrónico al constructor de la tarea, o pasar una expresión lambda que devuelve una acción asincrónico, el contexto predeterminado para todas las continuaciones de esa tarea es el contexto actual.Si la tarea no se construye a partir de una acción asincrónico, un contexto arbitrario se utiliza de forma predeterminada para las continuaciones de la tarea.Puede reemplazar el contexto predeterminado con la clase de concurrency::task_continuation_context.
En este documento
Crear operaciones asincrónicas
Ejemplo: Crear el componente en tiempo de ejecución de c++. Windows
Controlar el subproceso de ejecución
Ejemplo: Controlar la ejecución de una aplicación de almacén de Windows con C++ y XAML
Crear operaciones asincrónicas
Puede utilizar la tarea y el modelo de continuación en la biblioteca (PPL) de modelos de procesamiento Paralelo de definir tareas en segundo plano junto con las tareas adicionales que se ejecutan cuando la tarea anterior se complete.Esta funcionalidad se proporciona por la clase de concurrency::task.Para obtener más información sobre este modelo y la clase de task, vea Paralelismo de tareas (Runtime de simultaneidad).
Windows en tiempo de ejecución es una interfaz de programación que puede utilizar para crear aplicaciones de Tienda Windows que sólo se ejecutan en un entorno especial del sistema operativo.Tal uso de aplicaciones autorizó funciones, tipos de datos, y los dispositivos, y se distribuye de Tienda Windows.Windows en tiempo de ejecución es representado por la interfaz de binario de aplicación (ABI).ABI es un contrato binario subyacente que coloca Windows en tiempo de ejecución API a disposición de los lenguajes de programación como Visual C++.
Mediante Windows en tiempo de ejecución, puede utilizar las mejores características de lenguajes de programación diferentes y combinarlos en una aplicación.Por ejemplo, podría crear la interfaz de usuario en JavaScript y ejecute la lógica de cómputo- intensiva de la aplicación en componente de c++.La capacidad de realizar estas operaciones de cómputo- intensivas en segundo plano es factor clave en conservar la interfaz de usuario rápida.Dado que la clase de task es específica de C++, debe utilizar una interfaz de Windows en tiempo de ejecución para comunicar operaciones asincrónicas otros componentes (que pueden estar escritos en otros lenguajes distintos de C++).Windows en tiempo de ejecución proporciona cuatro interfaces que puede utilizar para representar las operaciones asincrónicas:
Windows::Foundation::IAsyncAction
Representa una acción asincrónico.Windows::Foundation::IAsyncActionWithProgress<TProgress>
Representa una acción asincrónico que avanzan los informes.Windows::Foundation::IAsyncOperation<TResult>
Representa una operación asincrónica que devuelve un resultado.Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Representa una operación asincrónica que devuelve un resultado y progreso de los informes.
La noción de acción significa que la tarea no genera un valor (piense en una función que devuelve void).La noción de una operación significa que la tarea genera un valor.La noción de progreso significa que la tarea puede generar mensajes de progreso al llamador.JavaScript, .NET Framework, y Visual C++ cada proporciona una manera de crear instancias de estas interfaces para el uso a través del límite de ABI.Para Visual C++, el runtime de simultaneidad proporciona la función de concurrency::create_async.Esta función crea una acción o una operación asincrónica de Windows en tiempo de ejecución que representan la finalización de una tarea.La función de create_async toma una función de trabajo (normalmente una expresión lambda), internamente crea un objeto de task, y los ajustes que tarea en una de las cuatro interfaces asincrónicas de Windows en tiempo de ejecución.
[!NOTA]
Utilice create_async sólo si tiene que crear la funcionalidad que se puede obtener acceso desde otro lenguaje u otro componente de Windows en tiempo de ejecución.Utilice la clase de task directamente cuando se sabe que la operación generada y utilizada por el código de C++ en el mismo componente.
El tipo de los argumentos determina el tipo de valor devuelto de create_async.Por ejemplo, si la función de trabajo no devuelve un valor y no indica el progreso, create_async devuelve IAsyncAction.Si la función de trabajo no devuelve un valor y también progreso informes, create_async devuelve IAsyncActionWithProgress.Para informar sobre el progreso, proporcione un objeto de concurrency::progress_reporter como parámetro a la función de trabajo.La capacidad de indicarle permisos de progreso para indicar qué cantidad de trabajo se realizó y aún permanece qué cantidad (por ejemplo, como un porcentaje).Permite también para informar sobre los resultados cuando están disponibles.
IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, y las interfaces cada uno de IAsyncActionOperationWithProgress<TProgress, TProgress> proporcionan un método de Cancel que permite cancelar la operación asincrónica.La clase de task funciona con tokens de cancelación.Cuando utilice un símbolo cancel para cancelar el trabajo, el runtime no inicia el nuevo trabajo suscrito a ese símbolo.Funcione que ya está activo puede controlar su token de cancelación y detener cuando puede.Este mecanismo se describe con mayor detalle en el documento Cancelación en la biblioteca PPL.Puede conectar la cancelación de tareas con los métodos de Windows en tiempo de ejecuciónCancel de dos maneras.Primero, puede definir la función de trabajo que se pasa a create_async para tomar un objeto de concurrency::cancellation_token.Cuando se llama al método de Cancel, este token de cancelación se cancela y las reglas habituales de cancelación se aplican al objeto subyacente de task que admite la llamada de create_async.Si no proporciona un objeto de cancellation_token, el objeto subyacente de task define uno implícitamente.Defina un objeto de cancellation_token cuando necesite de forma cooperativa responder a la cancelación en la función de trabajo.La sección Ejemplo: Controlar la ejecución de una aplicación de almacén de Windows con C++ y XAML muestra un ejemplo de cómo realizar la cancelación en una aplicación de Tienda Windows con C# y XAML que use un componente personalizado de Windows en tiempo de ejecución C++.
Precaución |
---|
En una cadena de continuaciones de tareas, limpiar siempre el estado y llame a concurrency::cancel_current_task cuando concurrency::is_task_cancellation_requested devuelve true.Si vuelve a en lugar de la llamada cancel_current_task, cambia de la operación al estado completado en lugar del estado cancelado. |
La tabla siguiente se resumen las combinaciones que puede utilizar para definir operaciones asincrónicas en la aplicación.
Para crear esta interfaz de Windows en tiempo de ejecución |
Devuelve este tipo de create_async |
Pase estos tipos de parámetro a la función de trabajo para utilizar un símbolo implícitamente de cancelación |
Pase estos tipos de parámetro a la función de trabajo para utilizar un símbolo explícito de cancelación |
---|---|---|---|
IAsyncAction |
void o task<void> |
(ninguno) |
(cancellation_token) |
IAsyncActionWithProgress<TProgress> |
void o task<void> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
IAsyncOperation<TResult> |
T o task<T> |
(ninguno) |
(cancellation_token) |
IAsyncActionOperationWithProgress<TProgress, TProgress> |
T o task<T> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
Puede devolver un valor o un objeto de task de la función de trabajo que se pasa a create_async la función.Estas variaciones representan distintos comportamientos.Cuando devuelve un valor, la función de trabajo se encapsula en task para poder ejecutarse en un subproceso de fondo.Además, task subyacente utiliza un símbolo implícitamente de cancelación.A la inversa, si devuelve un objeto de task, la función de trabajo ejecuta sincrónicamente.Por consiguiente, si devuelve un objeto de task, asegúrese de que cualquier operación larga en la función de trabajo también ejecute como tareas de habilitar la aplicación para seguir respondiendo.Además, task subyacente no utiliza un símbolo implícitamente de cancelación.Por consiguiente, debe definir la función de trabajo para tomar un objeto de cancellation_token si necesita compatibilidad con la cancelación cuando se devuelve un objeto de task de create_async.
El ejemplo siguiente se muestran las distintas maneras de crear un objeto de IAsyncAction que se puede utilizar por otro componente de Windows en tiempo de ejecución.
// 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.
});
});
[Parte superior]
Ejemplo: Crear el componente y usarlo en tiempo de ejecución de c++. Windows C#
Considere una aplicación que utiliza XAML y C# para definir la interfaz de usuario y el componente de c++. Windows en tiempo de ejecución para realizar operaciones cálculo- intensivas.En este ejemplo, los cálculos del componente de C++ que los números en un intervalo dado son primos.Para mostrar las diferencias entre las cuatro interfaces asincrónicas de la tarea de Windows en tiempo de ejecución, start, en Visual Studio, creando Solución en blanco y llamarlo Prepara.A continuación agregue a la solución un proyecto Componente en tiempo de ejecución de Windows y llamarlo PrimesLibrary.Agregue el código siguiente al archivo de encabezado generado de C++ (este ejemplo cambia Class1.h a Primes.h).Cada método de public define una de las cuatro interfaces asincrónicas.Los métodos que devuelven un retorno Windows::Foundation::Collections::IVector<int> de valor un objeto.Los métodos que proporcionan los valores de double de producción de progreso que definen el porcentaje del trabajo total completado.
#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);
};
}
[!NOTA]
Por convención, los nombres de método asincrónico en Windows en tiempo de ejecución normalmente finalizan con “Async”.
Agregue el código siguiente al archivo de código fuente generado de C++ (este ejemplo cambia Class1.cpp a Primes.cpp).La función de is_prime determina si la entrada es primero.Los métodos restantes implementan la clase de Primes.Cada llamada a create_async utiliza una firma compatible con el método del que se llama.Por ejemplo, porque Primes::ComputePrimesAsync devuelve IAsyncAction, la función de trabajo que se proporciona a create_async no devuelve un valor y no toma un objeto de progress_reporter como parámetro.
// 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;
});
}
Cada método realiza primero la validación para garantizar que los parámetros de entrada son no negativos.Si un valor de entrada es negativo, el método produce Platform::InvalidArgumentException.El control de errores se explica más adelante en esta sección.
Para utilizar estos métodos de una aplicación de Tienda Windows, use la plantilla de Visual c# Aplicación en blanco (XAML) para agregar un segundo proyecto a la solución de Visual Studio.Nombres de este ejemplo el proyecto Prepara.A continuación, proyecto de Prepara, agregue una referencia al proyecto de PrimesLibrary.
Agregue el código siguiente a MainPage.xaml.Este código define la interfaz de usuario de modo que pueda llamar a los resultados del componente y la presentación de C++.
<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>
Agregue el código siguiente a la clase de MainPage en MainPage.xaml.Este código define un objeto de Primes y los controladores de eventos de botón.
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();
}
Estos métodos utilizan las palabras clave de async y de await para actualizar la interfaz de usuario después de que las operaciones asincrónicas completado.Para obtener información sobre los modelos asincrónicos que están disponibles para C# y Visual Basic, vea Los modelos asincrónicos de Windows almacenan aplicaciones con C# y Los modelos asincrónicos de Windows almacenan aplicaciones con VB.
Los métodos de getPrimesCancellation y de cancelGetPrimes colaboran para permitir al usuario cancelar la operación.Cuando el usuario elige el botón cancelar, las llamadas al IAsyncOperationWithProgress<TResult, TProgress>::Cancel método de cancelGetPrimes para cancelar la operación.El runtime de simultaneidad, que administra la operación asincrónica subyacente, produce un tipo de excepción interno que serán detectados por Windows en tiempo de ejecución para comunicar que la cancelación ha finalizado.Para obtener más información sobre el modelo de cancelación, vea Cancelación en la biblioteca PPL.
Importante |
---|
Para permitir que el runtime de simultaneidad correctamente para notificar a Windows en tiempo de ejecución que ha cancelado la operación, no detecta este tipo de excepción interno.Esto significa que no también debe detectar todas las excepciones (catch (...)).Si debe detectar todas las excepciones, producir la excepción para garantizar que Windows en tiempo de ejecución puede completar la operación de cancelación. |
La ilustración siguiente se muestra la aplicación de Prepara después de cada opción se haya elegida.
Para obtener ejemplos que utilizan create_async para crear tareas asincrónicas que se pueden utilizar en otros lenguajes, vea Mediante C++ en el ejemplo del optimizador Bing Maps Trip y Windows 8 operaciones asincrónicas en C++ con PPL.
[Parte superior]
Controlar el subproceso de ejecución
Windows en tiempo de ejecución utiliza el modelo de subprocesos COM.En este modelo, los objetos se hospedan en apartamentos diferentes, dependiendo de cómo se controlan la sincronización.Los objetos seguros se hospedan en el apartamento multiproceso (MTA).Los objetos que deben tener acceso a un único subproceso se hospedan en un contenedor uniproceso (STA).
En una aplicación con una interfaz de usuario, el subproceso de ASTA (aplicación STA) es responsable de suministrar mensajes de ventana y es el único subproceso del proceso que puede actualizar los controles STA- hospedados de la interfaz de usuario.Esto tiene dos consecuencias.Primero, permitir que la aplicación siga respondiendo, todo el CPU- intensivo y operaciones de E/S no se deben ejecutar en el subproceso de ASTA.En segundo lugar, los resultados procedente de los subprocesos de fondo deben calcularse de nuevo al ASTA para actualizar la interfaz de usuario.En aplicaciones de c++. Tienda Windows, MainPage y otras páginas XAML todo el proceso en el ATSA.Por consiguiente, continuaciones de tareas que se declaran en el ASTA se ejecutan allí de forma predeterminada de modo que se puedan actualizar los controles directamente en el cuerpo de la continuación.Sin embargo, si se anida una tarea en otra tarea, cualquier continuación en esa tarea anidada se ejecuta en MTA.Por consiguiente, deberá considerar si explícitamente especificar en qué contexto ejecución de estas continuaciones.
Una tarea que se crea a partir de una operación asincrónica, como IAsyncOperation<TResult>, usa la semántica especial que puede ayudarle a omitir los detalles de subprocesamiento.Aunque una operación podría ejecutarse en un subproceso de fondo (o puede no ser respaldado por un subproceso en absoluto), sus continuaciones de forma predeterminada se garantiza para ejecutarse en el apartamento que inició las operaciones de continuación (es decir de apartamento que llamó task::then).Puede utilizar la clase de concurrency::task_continuation_context para controlar el contexto de ejecución de una continuación.Utilice estos métodos auxiliares estáticos para crear los objetos de task_continuation_context:
Uso concurrency::task_continuation_context::use_arbitrary de especificar que la continuación se ejecuta en un subproceso de fondo.
Uso concurrency::task_continuation_context::use_current de especificar que la continuación se ejecuta en el subproceso que llamó task::then.
Puede pasar un objeto de task_continuation_context al método de task::then para controlar explícitamente el contexto de ejecución de continuación o puede pasar la tarea a otro apartamento y después llamar al método de task::then para controlar implícitamente el contexto de ejecución.
Importante |
---|
Dado que el subproceso de la interfaz de usuario principal de las aplicaciones de Tienda Windows ejecuta en STA, las continuaciones que cree en que STA de forma predeterminada se ejecutan en el STA.En consecuencia, las continuaciones que cree en MTA ejecutan en MTA. |
La sección siguiente se muestra una aplicación que lee un archivo desde el disco, encuentra la mayoría de las palabras comunes en qué archivo y, a continuación se muestran los resultados en la interfaz de usuario.La operación final, actualizar la interfaz de usuario, aparece en el subproceso de la interfaz de usuario.
Importante |
---|
Este comportamiento es específico de las aplicaciones de Tienda Windows.Para las aplicaciones de escritorio, no controla donde se ejecutan las continuaciones.En su lugar, el programador elige un subproceso de trabajo en el que ejecutar cada continuación. |
Importante |
---|
No llame a concurrency::task::wait en el cuerpo de una continuación que se ejecute en el STA.Si no, el runtime produce concurrency::invalid_operation porque este método bloquea el subproceso actual y puede hacer que la aplicación para volverse no responde.Sin embargo, puede llamar al método de concurrency::task::get reciba el resultado de la tarea antecedente en una continuación tarea- basada en. |
[Parte superior]
Ejemplo: Controlar la ejecución de una aplicación de Tienda Windows con C++ y XAML
Considere la aplicación en cuestión. XAML que lee un archivo desde el disco, encuentra la mayoría de las palabras comunes en qué archivo y, a continuación se muestran los resultados en la interfaz de usuario.Para crear esta aplicación, start, en Visual Studio, creando un proyecto de Tienda WindowsEsconda la aplicación (XAML) y llamarlo CommonWords.En el manifiesto de aplicación, especifique la capacidad Biblioteca de documentos para permitir a la aplicación obtener acceso a la carpeta documentos.Agregue también el tipo de archivo de texto (.txt) a la sección de declaraciones del manifiesto de aplicación.Para obtener más información sobre las funciones y declaraciones de la aplicación, vea Paquetes e implementación de la aplicación.
Actualice el elemento de Grid en MainPage.xaml para incluir un elemento de ProgressRing y un elemento de TextBlock.ProgressRing indica que la operación está en curso y TextBlock muestra los resultados del cálculo.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ProgressRing x:Name="Progress"/>
<TextBlock x:Name="Results" FontSize="16"/>
</Grid>
Agregue las siguientes instrucciones de #include a pch.h.
#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>
Agregue las declaraciones de método siguientes a la clase de 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);
Agregue las siguientes instrucciones de using a MainPage.cpp.
using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
En MainPage.cpp, implemente MainPage::MakeWordList, MainPage::FindCommonWords, y los métodos de MainPage::ShowResults.MainPage::MakeWordList y MainPage::FindCommonWords realizan operaciones de cómputo- intensivas.El método de MainPage::ShowResults muestra el resultado del cálculo en la interfaz de usuario.
// 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());
}
Modifique el constructor de MainPage para crear una cadena de las tareas de continuación que presente en la interfaz de usuario las palabras comunes en el libro La Ilíada de homero.Las dos primeras tareas de continuación, que dividen el texto en palabras individuales y palabras comunes de búsqueda, pueden ser largas y por consiguiente se establezca explícitamente para ejecutar en segundo plano.La tarea final de continuación, que actualiza la interfaz de usuario, no especifica ningún contexto de continuación, y por consiguiente sigue las reglas de subproceso controlado.
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.
});
}
[!NOTA]
Este ejemplo muestra cómo especificar contextos de ejecución y cómo crear una cadena de continuaciones.Llame de nuevo a que de forma predeterminada una tarea que se crea a partir de una operación asincrónica ejecute sus continuaciones en el apartamento que llamó task::then.Por consiguiente, este ejemplo utiliza task_continuation_context::use_arbitrary para especificar que las operaciones que no implican la interfaz de usuario están realizadas en un subproceso de fondo.
La ilustración siguiente muestra los resultados de la aplicación de CommonWords.
En este ejemplo, es posible admitir la cancelación porque los objetos de task que uso admiten de create_async un símbolo implícitamente de cancelación.Defina la función de trabajo para tomar un objeto de cancellation_token si las tareas deben responder a la cancelación de manera cooperativa.Para obtener más información sobre la cancelación en PPL, vea Cancelación en la biblioteca PPL
[Parte superior]