Creazione di operazioni asincrone in C++ per le applicazioni Windows Store
In questo documento, sono descritti alcuni punti chiave che devono essere ricordati quando si utilizza il runtime di concorrenza per produrre operazioni asincrone in un'applicazione Windows Store.
L'utilizzo della programmazione asincrona è una componente chiave nel modello di applicazione Windows Store, perché consente alle applicazioni di rimanere attive per l'inserimento dell'input da parte dell'utente.È possibile avviare un'attività di lunga durata senza bloccare la thread dell'interfaccia utente e ricevere successivamente i risultati dell'attività.È inoltre possibile annullare le attività e ricevere notifiche di stato delle attività che vengono eseguite in background.Nel documento Programmazione asincrona in C++, viene descritto il modello asincrono disponibile in Visual C++ per creare applicazioni Windows Store.Nel documento, viene illustrato com'è possibile utilizzare e creare catene di operazioni asincrone di Windows Runtime.In questa sezione, viene descritto come utilizzare il runtime di concorrenza per produrre operazioni asincrone che possono essere utilizzate da altri componenti di Windows Runtime e come controllare il lavoro asincrono eseguito.Considerare anche la lettura di suggerimenti e modelli di programmazione asincrona in Hilo (app di Windows Store che utilizzano C++ e XAML) per imparare ad utilizzare il runtime di concorrenza per implementare operazioni asincrone in Hilo, un'applicazione Windows Store che utilizza C++ e XAML.
[!NOTA]
È possibile utilizzare Parallel Patterns Library (PPL) e Libreria di agenti asincroni, in un'applicazione Windows Store.Tuttavia, non è possibile utilizzare l'Utilità di pianificazione o il Gestione risorse.Questo documento, descrive le funzionalità aggiuntive che il runtime di concorrenza mette a disposizione solamente per un'applicazione Windows Store e non per un'applicazione desktop.
Punti chiave
Utilizzare concurrency::create_async per creare operazioni asincrone, che possono essere utilizzate da altri componenti (che possono essere scritti in linguaggi diversi dal C++).
Utilizzare concurrency::progress_reporter per segnalare le notifiche dello stato di avanzamento dei componenti che chiamano operazioni asincrone.
Utilizzare i token di annullamento per abilitare le operazioni asincrone interne di annullamento.
Il comportamento della funzione create_async, dipende dal tipo restituito della funzione lavoro che gli viene passata.Una funzione lavoro che restituisce un'attività ( task<T> o task<void>) esegue in modo sincrono il contesto della chiamata create_async.Una funzione lavoro che restituisce T o void, viene eseguita in un contesto arbitrario.
È possibile utilizzare il metodo concurrency::task::then, per creare una catena di attività che vengono eseguite una dopo l'altra.In un'applicazione Windows Store il contesto predefinito per le continuazioni di attività, dipende da come l'attività è stata creata.Se l'attività è stata creata passando un'azione asincrona al costruttore dell'attività, oppure passare una lambda espressione che restituisce un'azione asincrona, quindi il contesto attuale è il contesto predefinito della continuazione di tale attività.Se l'attività non viene costruita da un'azione asincrona, viene utilizzato per impostazione predefinita un contesto arbitrario per la continuazione di un'attività.È possibile, sovrascrivere il contesto predefinito con la classe concurrency::task_continuation_context.
In questo documento
Creare operazioni asincrone
Esempio: Creare un componente Windows Runtime in C++
Controllo la Thread di esecuzione
Esempio: Controllare l'esecuzione di un'applicazione Windows Store con C++ e XAML
Creare operazioni asincrone
È possibile utilizzare l'attività e il modello di continuazione Parallel Patterns Library (PPL), per definire le attività di background e le attività aggiuntive che vengono eseguite quando l'attività precedente viene completata.Questa funzionalità viene fornita dalla classe concurrency::task.Per ulteriori informazioni su questo modello e sulla classe task, vedere Parallelismo delle attività (runtime di concorrenza).
Il Windows Runtime è un'interfaccia di programmazione da utilizzare per creare applicazioni Windows Store, che vengono eseguite esclusivamente nell'ambiente di un sistema operativo specifico.Tali applicazioni utilizzando funzioni autorizzate, tipi di dati e dispositivi che vengono distribuiti da Windows Store.Il Windows Runtime è rappresentato da una Interfaccia applicativa binaria (ABI).ABI è un contratto binario sottostante che rende le Windows Runtime API disponibili a linguaggi di programmazione come Visual C++.
Utilizzando Windows Runtime, è possibile utilizzare le migliori funzionalità di diversi linguaggi di programmazione e combinarle in un unica applicazione.Ad esempio, è possibile creare l'interfaccia utente in JavaScript ed eseguire un'applicazione logica più complessa dal punto di vista dell'elaborazione, in un componente C++.La possibilità di eseguire questa operazione più complessa dal punto di vista dell'elaborazione, in background è un fattore chiave per rendere reattiva l'interfaccia utente.Poiché la classe task, è specifica nel linguaggio C++ è necessario utilizzare l'interfaccia Windows Runtime per comunicare operazioni asincrone ad altri componenti (che potrebbero essere scritti in linguaggi diversi da C++).Il Windows Runtime fornisce quattro interfacce che possono essere utilizzate per rappresentare le operazioni asincrone:
Windows::Foundation::IAsyncAction
Rappresenta un'azione asincrona.Windows::Foundation::IAsyncActionWithProgress<TProgress>
Rappresenta un'azione asincrona che restituisce lo stato di avanzamento.Windows::Foundation::IAsyncOperation<TResult>
Rappresenta un'operazione asincrona che restituisce un risultato.Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Rappresenta un'operazione asincrona che restituisce un risultato e un report sullo stato di avanzamento.
La nozione di un azione implica che l'attività asincrona non produce un valore (pensare ad una funzione che restituisce void).La nozione di un' operazione implica che l'attività asincrona produce un valore.La nozione di stato indica che l'attività può segnalare messaggi di stato al chiamante.JavaScript, .NET Framework e Visual C++ offrono la possibilità di creare istanze di queste interfacce, da utilizzare come limite ABI.Per Visual C++, il runtime di concorrenza fornisce la funzione concurrency::create_async.Questa funzione crea un'azione asincrona Windows Runtime o un'operazione che rappresenta il completamento di un'attività.La funzione di create_async accetta una funzione lavoro (in genere una lambda espressione), crea internamente un oggetto task che viene incluso in una delle quattro interfacce asincrone Windows Runtime.
[!NOTA]
Utilizzare create_async solo quando è necessario per creare funzionalità che possono essere accedute da un'altro linguaggio o da un'altro componente Windows Runtime.Utilizzare direttamente la classe task quando si sa che l'operazione viene prodotta da entrambi e utilizzata il codice C++ nello stesso componente.
Il tipo restituito create_async è determinato dal tipo degli argomenti.Ad esempio, se la funzione lavoro non restituire un valore e lo stato di avanzamento create_async, restituisce IAsyncAction.Se la funzione lavoro non restituisce un valore e lo stato di avanzamento create_async, restituisce IAsyncActionWithProgress.Per indicare lo stato di avanzamento, viene fornito un oggetto concurrency::progress_reporter come parametro per la funzione lavoro.La possibilità di segnalare lo stato di avanzamento consente di segnalare la quantità di lavoro che viene eseguita e la quantità rimanente (ad esempio, come percentuale).Consente inoltre di segnalare i risultati non appena diventano disponibili.
Il IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> e le interfacce IAsyncActionOperationWithProgress<TProgress, TProgress> forniscono un metodo Cancel che consente di annullare l'operazione asincrona.La classe di lavoro task con i token di annullamento.Quando si utilizza un token di annullamento per annullare il lavoro il runtime non avvia il lavoro che sovrascrive il token.Il lavoro che è già attivo può monitorare, annullare i token e quando possibile arrestarli.Questo meccanismo viene illustrato più dettagliatamente nel documento Annullamento nella libreria PPL.È possibile connettersi all'attività di annullamento con i metodi Windows Runtime Cancel in due modalità.Innanzitutto, è possibile definire una funzione lavoro passata per create_async per acquisire un oggetto concurrency::cancellation_token.Quando il metodo Cancel viene chiamato, questo token di annullamento viene annullato e le regole standard di annullamento vengono applicante all'oggetto task che supporta la chiamata create_async.Se non si fornisce un oggetto cancellation_token, l'oggetto sottostante task viene definito in modo implicito.Definire un oggetto cancellation_token, quando è necessario rispondere all'annullamento nella funzione di lavoro.La sezione Esempio: Controllo dell'esecuzione in un'applicazione Windows Store con C++ e XAML riporta un esempio di come eseguire l'annullamento in un'applicazione Windows Store con C# e XAML che utilizza componenti personalizzati Windows Runtime.
Attenzione |
---|
In una catena di continuazioni bisogna sempre pulire lo stato e quindi chiamare concurrency::cancel_current_task quando concurrency::is_task_cancellation_requested restituisce trueSe si ritorna prima della chiamata cancel_current_task, le operazioni di transizione sono allo stato completo invece dello stato di annullamento. |
Nella seguente tabella vengono riepilogate le combinazioni che possono essere utilizzate per definire operazioni asincrone nell'applicazione.
Per creare questa interfaccia Windows Runtime. |
Restituire questo tipo da create_async |
Passare questi tipi di parametro alla funzione lavoro per utilizzare implicitamente il token di annullamento. |
Passare questi tipi di parametro alla funzione lavoro per utilizzare esplicitamente il token di annullamento. |
---|---|---|---|
IAsyncAction |
void o task<void> |
(nessuno) |
(cancellation_token) |
IAsyncActionWithProgress<TProgress> |
void o task<void> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
IAsyncOperation<TResult> |
T o task<T> |
(nessuno) |
(cancellation_token) |
IAsyncActionOperationWithProgress<TProgress, TProgress> |
T o task<T> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
È possibile restituire un valore o un oggetto task dalla funzione lavoro passata alla funzione create_async.Queste variazioni producono comportamenti diversi.Quando viene restituito un valore, la funzione lavoro viene sottoposta a wrapping in task in modo che possa essere eseguita su una thread in background.Inoltre, il task sottostante utilizza un token di annullamento implicito.Viceversa, viene restituito un oggetto di task e la funzione lavoro viene eseguita in modo sincrono.Pertanto, se viene restituito un oggetto task, assicurarsi che tutte le operazioni lunghe nella funzione lavoro possano essere eseguite nell'applicazione rimanendo reattive.Inoltre, task sottostante non utilizza un token di annullamento implicito.Pertanto, è necessario definire la funzione lavoro per accettare un oggetto cancellation_token se è necessario il supporto per l'annullamento quando si ritorna a task da un oggetto create_async.
Nell'esempio seguente, vengono mostrati vari modi per creare un oggetto IAsyncAction che può essere utilizzato da un altro componente 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.
});
});
Top
Esempio: Creazione di un componente Windows Runtime in C++ e utilizzarlo in C#
Si consideri un'applicazione che utilizza XAML e C#, per definire l'interfaccia utente e un componente Windows Runtime in C++, per eseguire operazioni intensive di calcolo.In questo esempio, il componente C++ computa i numeri in un intervallo specifico di numeri primi.Per illustrare le differenze tra le quattro interfacce dell'attività asincrona Windows Runtime, avviare Visual Studio, creando Soluzione vuota e denominandolo Primi.Aggiungere alla soluzione un progetto Componente di Windows Runtime e denominarlo PrimesLibrary.Aggiungere il codice seguente al file di intestazione generato in C++ (in questo esempio viene rinominata da Class1.h a Primes.h).Ogni metodo public definisce una delle quattro interfacce asincrone.I metodi che restituiscono un valore di ritorno Windows::Foundation::Collections::IVector<int> da un oggetto.I metodi che segnalano lo stato di avanzamento producono valori double che definiscono la percentuale di lavoro globale completata.
#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]
Per convenzione, i nomi dei metodi asincroni in Windows Runtime in genere terminano con "Async".
Aggiungere il seguente codice al file sorgente generato in C++ (in questo esempio viene rinominata da Class1.cpp a Primes.cpp).La funzione is_prime determina se il relativo input è un numero primo.I metodi rimanenti implementano la classe Primes.Ogni chiamata a create_async utilizza una firma che deve essere compatibile con il metodo che viene chiamato.Ad esempio, poiché Primes::ComputePrimesAsync restituisce IAsyncAction, la funzione lavoro che è fornita per create_async non restituisce un valore e non accetta un oggetto progress_reporter come parametro.
// 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;
});
}
Ogni metodo verifica innanzitutto la validità assicurandosi che i parametri in input non siano negativi.Se il valore di input è negativo, il metodo genera Platform::InvalidArgumentException.La gestione degli errori è descritta in una sezione successiva.
Per utilizzare questi metodi in un'applicazione Windows Store, utilizzare il modello Visual C# Applicazione vuota (XAML) per aggiungere un secondo progetto alla soluzione Visual Studio.In questo esempio denominare il progetto Primi.Quindi, dal progetto Primi, aggiungere un riferimento al progetto PrimesLibrary.
Aggiungere il codice seguente al file MainPage.xaml.Questo codice definisce l'interfaccia utente, rendendo possibile chiamare componenti C++ e visualizzare i risultati.
<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>
Aggiungere il codice seguente alla classe MainPage nel file MainPage.xaml.Questo codice definisce un oggetto Primes e il gestore eventi del pulsante.
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();
}
Questi metodi utilizzano le parole chiave await e async, per aggiornare l'interfaccia utente dopo il completamento della operazione asincrone.Per informazioni sui modelli asincroni disponibili in C# e Visual Basic, vedere Modelli asincroni in applicazioni Windows Store con il C e Modelli asincroni in applicazioni Windows Store con VB.
I metodi cancelGetPrimes e di getPrimesCancellation interagiscono per consentire all'utente di annullare l'operazione.Quando l'utente sceglie il pulsante Annulla, le chiamate al metodo IAsyncOperationWithProgress<TResult, TProgress>::Cancel per annullare l'operazione cancelGetPrimes.Il runtime di concorrenza, che gestisce l'operazione asincrona sottostante genera un tipo di eccezione interna che viene intercettata da Windows Runtime per comunicare che l'annullamento è stata completato.Per ulteriori informazioni sul modello di annullamento, vedere Annullamento nella libreria PPL.
Importante |
---|
Per abilitare il runtime di concorrenza correttamente segnalare per il Windows Runtime che ha annullato l'operazione, di non catturare questo tipo di eccezioni interne.Ciò significa che non è possibile rilevare tutte le eccezioni (catch (...)).Se è necessario rilevare tutte le eccezioni, rigenerare l'eccezione per garantire che il Windows Runtime possa completare l'operazione di annullamento |
Di seguito viene illustrata l'applicazione Primi dopo che ogni opzione è stata scelta.
Per esempi che utilizzano create_async, per creare attività asincrone che possono essere utilizzate da altri linguaggi, vedere la pagina Utilizzare C++ nell'utilità di ottimizzazione dei viaggi in Bing Maps e Operazioni asincrone in Windows 8 in C++ con la libreria PPL.
Top
Controllo la Thread di esecuzione
Il Windows Runtime utilizza il modello di threading COM.In questo modello, gli oggetti sono ospitati in apartment diversi, a seconda di come gli handle sono sincronizzati.Gli oggetti thread-safe sono ospitati in apartment multithreading (MTA).Gli oggetti che devono essere utilizzati da una singola thread sono ospitati in un apartment single-threaded (STA).
In un'applicazione che utilizza un'interfaccia utente, la thread di ASTA (Applicazione STA) è responsabile della distribuzione di messaggi della finestra ed è l'unica thread del processo ad aggiornare i controlli dell'interfaccia utente ospitata in STA.Ciò ha due conseguenze.Innanzitutto, consentire all'applicazione di rimanere attiva qualsiasi utilizzo intensivo della CPU e delle operazioni di I/O non deve essere eseguito in una thread ASTA.In secondo luogo, i risultati generati dalle thread di background devono eseguire il marshalling per ASTA per aggiornare l'interfaccia utente.In un'applicazione C++ Windows Store, MainPage e le altre pagine XAML vengono tutte eseguite in ATSA.Pertanto, le continuazioni di attività che vengono dichiarate nel ASTA sono eseguite per impostazione predefinita, in modo da poter aggiornare i controlli direttamente nel corpo di continuazione.Tuttavia, se annidate un'attività in un'altra attività tutte le continuazioni su tale attività annidata operano in un MTA.Pertanto, è necessario considerare se specificare, in modo esplicito su tale contesto l'esecuzione di tali continuazioni.
Un'attività creata da un'operazione asincrona come IAsyncOperation<TResult>, utilizza una speciale semantica che consente di ignorare i dettagli di threading.Anche se un'operazione può essere eseguita su una thread in background (oppure non può essere supportata da una a tutte thread), le continuazioni per impostazione predefinita vengo eseguite nel apartment che aveva avviato le operazioni di continuazione(in altre parole, nel apartment denominato task::then).È possibile utilizzare la classe concurrency::task_continuation_context per controllare il contesto di esecuzione di una continuazione.Utilizzare questi metodi di supporto per creare un oggetto task_continuation_context:
Utilizzare concurrency::task_continuation_context::use_arbitrary per specificare che la continuazione viene eseguito su una thread in background.
Utilizzare concurrency::task_continuation_context::use_current per specificare che la continuazione viene eseguito sulla thread che ha chiamato task::then.
È possibile passare un oggetto task_continuation_context, al metodo task::then per controllare esplicitamente il contesto di esecuzione della continuazione oppure passare l'attività a un altro apartment, quindi chiamare il metodo task::then per controllare implicitamente il contesto di esecuzione.
Importante |
---|
Poiché, la thread principale dell'interfaccia utente di applicazioni Windows Store viene eseguita in STA, le continuazioni che sono create in STA per impostazione predefinita vengono eseguite nella STA.Pertanto, le continuazioni create sulla MTA vengono eseguite sulla MTA. |
Nella seguente sezione viene illustrata un'applicazione che legge un file dal disco, cerca il maggior numero di parole comuni all'interno del file e visualizza il risultato nell'interfaccia utente.L'operazione finale, aggiorna l'interfaccia utente che viene eseguita nella thread UI.
Importante |
---|
Questo comportamento è specifico delle applicazioni Windows Store.Per le applicazioni desktop, non controllare dove le continuazioni vengono eseguite.Invece, l'utilità di pianificazione sceglie una thread di lavoro per ogni continuazione che deve essere eseguita. |
Importante |
---|
Non chiamare concurrency::task::wait nel corpo di una continuazione che viene eseguita su STA.In caso contrario, il runtime visualizza concurrency::invalid_operation poiché questo metodo blocca la thread corrente e può determinare l'applicazione che non risponde.Tuttavia, è possibile chiamare il metodo concurrency::task::get per ricevere il risultato dell'attività precedente in una continuazione relativa alle attività. |
Top
Esempio: Esecuzione del controllo di un'applicazione Windows Store con C++ e XAML
Considerare un'applicazione C++ XAML, che legge un file dal disco, cerca le parole maggiormente presenti nel file e visualizza il risultato nell'interfaccia utente.Per creare questa l'applicazione, avviare Visual Studio, creando un progetto Windows Store Applicazione vuota (XAML) e denominandolo CommonWords.Nel file manifest dell'applicazione, specificare la funzionalità Raccolta documenti per consentire all'applicazione di accendere alla cartella Documenti.Aggiungere inoltre il tipo di file di testo (.txt) nella sezione dichiarazioni nel file manifest dell'applicazione.Per ulteriori informazioni sulle funzionalità e le dichiarazioni dell'applicazione, vedere la pagina Pacchetti e distribuzione di applicazioni.
Aggiornare l'elemento Grid nel file MainPage.xaml per includere un elemento ProgressRing e un elemento TextBlock.Il ProgressRing indica che l'operazione è in corso e TextBlock visualizza i risultati del calcolo.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ProgressRing x:Name="Progress"/>
<TextBlock x:Name="Results" FontSize="16"/>
</Grid>
Aggiungere le seguenti istruzioni #include in pch.h.
#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>
Aggiungere le seguenti dichiarazioni di metodo nella classe 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);
Aggiungere le seguenti istruzioni using in MainPage.cpp.
using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
In MainPage.cpp, implementare i metodi MainPage::MakeWordList, MainPage::FindCommonWords e MainPage::ShowResults.Il MainPage::MakeWordList e MainPage::FindCommonWords eseguono operazioni con computazioni più complesse.Il metodo MainPage::ShowResults, visualizzare il risultato del calcolo nell'interfaccia utente.
// 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());
}
Modificare il costruttore MainPage per creare una catena delle attività di continuazione che venga visualizzata nell'interfaccia utente, le parole più comuni nel libro Iliade di Omero.Le prime due attività di continuazione, che dividono il testo in singole parole e trovano le parole più comuni, possono richiedere parecchio tempo e quindi possono essere impostate esplicitamente per essere eseguite in background.L'attività di continuazione finale, che aggiorna l'interfaccia utente, non specifica un contesto di continuazione e pertanto segue le regole di threading 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.
});
}
[!NOTA]
In questo esempio, viene illustrato come specificare i contesti di esecuzione e come comporre una catena di continuazioni.Tenere presente che per impostazione predefinita un'attività viene creata da un'operazione asincrona ed esegue le continuazioni nel apartment chiamato task::then.Pertanto, in questo esempio viene utilizzato task_continuation_context::use_arbitrary, per specificare le operazioni che non incorporano un'interfaccia utente e che vengono eseguite in una thread di background.
Nell'illustrazione riportata di seguito, vengono mostrati i risultati dell'applicazione CommonWords.
In questo esempio, è possibile supportare l'annullamento perché gli oggetti task che supportano create_async e usano un annullamento implicito dei token.Definire la funzione lavoro per accettare un oggetto cancellation_token, se le attività devono rispondere all'annullamento in modo cooperativo.Per ulteriori informazioni sull'annullamento in PPL, vedere Annullamento nella libreria PPL.
Top