Erstellen von asynchronen Vorgängen in C++ für Windows Store-Apps
In diesem Dokument werden einige der Punkte, um zu sehen, wenn Sie die Concurrency Runtime verwenden, um asynchrone Vorgänge in einer Windows Store-App zu erzeugen.
Die Verwendung von asynchrone Programmierung ist eine wichtige Komponente im Windows Store-App-Modell, da er App ermöglicht, die Benutzereingabe bleibt reaktionsfähig.Sie können eine Aufgabe beginnen mit langer Laufzeit, ohne den UI-Thread zu blockieren, und Sie können die Ergebnisse der Aufgabe später empfangen.Sie können Aufgaben auch abbrechen und Statusbenachrichtigungen als Aufgaben empfangen, die im Hintergrund ausgeführt werden.Das Dokument Asynchrone Programmierung in C++ enthält eine Übersicht über das asynchrone Muster, das in Visual C++ verfügbar ist, Windows Store-App zu erstellen.In diesem Dokument wird erläutert, wie zu Ketten von asynchronen Vorgängen Windows-Runtime nutzen Sie und erstellen Sie.In diesem Abschnitt wird beschrieben, wie die Concurrency Runtime verwendet, um asynchrone Vorgänge, die von einer anderen Komponente Windows-Runtime genutzt werden können und wie zu erzeugen steuert, wie asynchrone Arbeit ausgeführt wird.Ziehen Sie außerdem zu lesen Asynchrone Programmierung Muster und Tipps in Hilo (Windows Store-Apps mithilfe von C++ und XAML), um zu erfahren, wie Sie die Concurrency Runtime verwenden, um asynchrone Vorgänge in Hilo, eine Windows Store-App mithilfe von C++ und XAML zu implementieren.
Hinweis |
---|
Sie können das Parallel Patterns Library (PPL) und Asynchronous Agents Library in einer Windows Store-App verwenden.Sie können jedoch den Taskplaner oder den Ressourcen-Manager nicht verwenden.In diesem Dokument werden zusätzliche Funktionen, die die Concurrency Runtime, das nur zu einer Windows Store-App verfügbar und nicht zu einer Desktop-App bereitstellt. |
Punkte
Verwenden Sie concurrency::create_async, um asynchrone Vorgänge zu erstellen, die können von anderen Komponenten (geschrieben werden die möglicherweise verwendet werden in anderen Sprachen als C++).
Verwendung concurrency::progress_reporter, Statusbenachrichtigungen Komponenten zu melden, die für asynchrone Vorgänge aufrufen.
Verwenden Sie Abbruchtoken, um interne asynchrone Vorgänge ermöglichen, abzubrechen.
Das Verhalten der create_async-Funktion hängt vom Rückgabetyp der Arbeitsfunktion ab, die dem übergeben wird.Eine Arbeitsfunktion, die Ausführungen einer Aufgabe (entweder task<T> oder task<void>) synchron im Kontext zurückgibt, der create_async aufgerufen wurde.Eine Arbeitsfunktion, die T oder void zurückgibt, wird in einen beliebigen Kontext.
Sie können die concurrency::task::then-Methode verwenden, um eine Kette von Aufgaben zu erstellen, die nacheinander ausgeführt werden.In einer Windows Store-App hängt der standardmäßige Kontext für die Fortsetzungen einer Aufgabe davon ab, wie diese Aufgabe erstellt wurde.Wenn die Aufgabe erstellt wurde, indem Sie eine asynchrone Aktion zum Aufgabenkonstruktor übergeben oder mit einem Lambda-Ausdruck übergeben, der eine asynchrone Aktion zurückgibt, ist der Standard Kontext für alle Fortsetzungen dieser Aufgabe der aktuelle Kontext.Wenn die Aufgabe nicht von einer asynchronen Aktion erstellt wird, wird ein beliebiger Kontext standardmäßig für die Fortsetzungen der Aufgabe verwendet.Sie können die Standardeinstellung Kontext mit der concurrency::task_continuation_context-Klasse überschreiben.
Inhalt dieses Dokuments
Erstellen von asynchronen Operationen
Beispiel: Erstellen der Windows Runtime-Komponente in C++
Steuern des Ausführungs-Threads
Beispiel: Steuern der Ausführung in einer Windows Store-App mit C++ und XAML
Erstellen von asynchronen Operationen
Sie können die Aufgabe und das Fortsetzungsmodell in der Parallel Patterns Library (PPL) verwenden Hintergrundaufgaben sowie zusätzliche Aufgaben definieren, die ausgeführt werden, wenn die vorherige Aufgabe ausführt.Diese Funktionalität wird von der - Klasse concurrency::task bereitgestellt.Weitere Informationen über dieses Modell und die task-Klasse, finden Sie unter Aufgabenparallelität (Concurrency Runtime).
Windows-Runtime ist eine Programmierschnittstelle, die Sie verwenden können, um Windows Store-App zu erstellen, die nur in einer speziellen Betriebssystemumgebung ausgeführt werden.Solche autorisierte App-Verwendung funktioniert, Datentypen und Geräte und wird von Windows Store verteilt.Windows-Runtime wird durch die - ABI (Application Binary Interface) dargestellt.Die ABI ist eine zugrunde liegende binärer Vertrag, der Windows-Runtime APIs so für Programmiersprachen wie Visual C++ macht.
Mit Windows-Runtime verwenden, können Sie die besten Funktionen verschiedener Programmiersprachen verwenden und in eine App kombinieren.Beispielsweise können Sie das Benutzeroberfläche in JavaScript erstellen und die Computer-intensive App-Logik Komponente in einer C++-Datei aus.Die Möglichkeit, diese rechenintensiven Vorgänge im Hintergrund auszuführen ist ein Schlüsselfaktor, wenn sie das Benutzeroberfläche reaktionsfähig enthält.Da die task-Klasse zu C++ spezifisch ist, müssen Sie eine Windows-Runtime-Schnittstelle verwenden, um asynchrone Vorgänge zu anderen Komponenten (die möglicherweise mitzuteilen in anderen Sprachen als C++ geschrieben werden).Windows-Runtime stellt vier Schnittstellen, die Sie verwenden können, um asynchrone Vorgänge darzustellen:
Windows::Foundation::IAsyncAction
Stellt eine asynchrone Aktion dar.Windows::Foundation::IAsyncActionWithProgress<TProgress>
Stellt eine asynchrone Aktion dar, die den Status gemeldet.Windows::Foundation::IAsyncOperation<TResult>
Stellt einen asynchronen Vorgang dar, der ein Ergebnis zurückgibt.Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Stellt einen asynchronen Vorgang dar, der ein Ergebnis zurückgibt und Berichte hinzuzufügen.
Der Begriff einer Aktion bedeutet, dass die asynchrone Aufgabe keinen Wert erzeugt (denken an eine Funktion, die void zurückgibt).Der Begriff eines Vorgangs bedeutet, dass die asynchrone Aufgabe einen Wert generiert.Der Begriff des Status bedeutet, dass die Aufgabe Statusmeldungen den Aufrufer melden kann.JavaScript, .NET Framework und Visual C++ jedes bietet eine eigene Methode, Instanzen dieser Schnittstellen zur Verwendung auf der ABI-Grenze zu erstellen.Für Visual C++ stellt die Concurrency Runtime die concurrency::create_async-Funktion.Diese Funktion erstellt eine asynchrone Aktion oder einen Vorgang Windows-Runtime, die den Abschluss einer Aufgabe darstellt.Die create_async-Funktion akzeptiert eine Arbeitsfunktion (in der Regel einen Lambda-Ausdruck), stellt intern ein task-Objekt umschlossen und erstellt, die in einer der vier asynchronen Windows-Runtime-Schnittstellen eine Aufgabe zuweisen.
Hinweis |
---|
Verwenden Sie create_async nur, wenn Sie Funktionen erstellen müssen, auf die in einer anderen Sprache oder von einer anderen Komponente Windows-Runtime zugegriffen werden kann.Verwenden Sie die task-Klasse direkt, wenn Sie wissen, dass der Vorgang von C++-Code in der gleichen Komponente erzeugt und verwendet wird. |
Der Rückgabetyp von create_async wird durch den Typ der Argumente bestimmt.Wenn die Arbeitsfunktion keinen Wert zurückgibt und nicht Status meldet, gibt create_asyncIAsyncAction zurück.Wenn die Arbeitsfunktion keinen Wert zurückgibt und auch Berichte hinzuzufügen, gibt create_asyncIAsyncActionWithProgress zurück.Um zu melden Status, stellen Sie eine concurrency::progress_reporter-Objekt als Parameter an die Arbeitsfunktion bereit.Die Fähigkeit, gemeldet Status ermöglicht es Ihnen, zu melden, welcher Arbeitsaufwand ausgeführt wurde und über die Menge weiterhin bleibt (beispielsweise, als Prozentsatz).Sie können Sie auch, um für die Ergebnisse, sobald sie verfügbar sind.
IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> und IAsyncActionOperationWithProgress<TProgress, TProgress>-Schnittstellen bieten eine Cancel jede Methode, die es Ihnen ermöglicht, den asynchronen Vorgang abzubrechen.Die task-Klasse funktioniert mit Abbruchtoken.Wenn Sie ein Abbruchtoken verwenden, um Aufgaben mit, beginnt die Laufzeit nicht neues bearbeiten, die diesem Token abonniert.Arbeiten Sie, das bereits kann sein Abbruchtokens überwachen und beenden aktiv ist, wenn es kann.Dieser Mechanismus wird ausführlich im Dokument Abbruch in der PPL beschrieben.Sie können der Aufgabenabbruch mit den Methoden Windows-RuntimeCancel auf zwei Arten herstellen.Erstens können Sie die Arbeitsfunktion definieren, die Sie zu create_async übergeben, um zu verwenden concurrency::cancellation_token einem Objekt.Wenn die Cancel-Methode aufgerufen wird, wird dieses Abbruchtoken abgebrochen und die normalen Abbruchsregeln gelten für zugrunde liegende task das Objekt, das den create_async Aufruf unterstützt.Wenn Sie kein cancellation_token-Objekt bereitstellen, definiert das zugrunde liegende Objekt ein task implizit.Definieren Sie ein - Objekt cancellation_token, wenn Sie auf Abbrüche in der Arbeitsfunktion kooperativ reagieren müssen.Der Abschnitt Beispiel: Steuern der Ausführung in einer Windows Store-App mit C++ und XAML ist ein Beispiel dafür, wie Abbruch in einer Windows Store-App mit C# und XAML ausführt, das eine benutzerdefinierte Komponente Windows-Runtime C++ verwendet.
Vorsicht |
---|
In einer Kette von Aufgabenfortsetzungen, räumen Sie immer Zustand auf und dann concurrency::cancel_current_task auf, wenn concurrency::is_task_cancellation_requestedtrue zurückgibt.Wenn Sie früh zurückkehren, anstatt, cancel_current_task aufzurufen, die Vorgangsübergänge in den Zustand anstelle des abgebrochenen Zustand. |
In der folgenden Tabelle werden die Kombinationen zusammengefasst, die Sie verwenden können, um asynchrone Vorgänge in der Anwendung zu definieren.
Um diese Windows-Runtime-Schnittstelle erstellen |
Geben Sie diesen Typ von create_async zurück |
Führen Sie diese Parametertypen mithilfe der Arbeitsfunktion, um ein implizites Abbruchtoken zu verwenden |
Führen Sie diese Parametertypen mithilfe der Arbeitsfunktion, um ein explizites Abbruchtoken zu verwenden |
---|---|---|---|
IAsyncAction |
void oder task<void> |
(kein) |
(cancellation_token) |
IAsyncActionWithProgress<TProgress> |
void oder task<void> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
IAsyncOperation<TResult> |
T oder task<T> |
(kein) |
(cancellation_token) |
IAsyncActionOperationWithProgress<TProgress, TProgress> |
T oder task<T> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
Sie können einen Wert oder einen task-Objekt von der Arbeitsfunktion zurückgeben, die Sie zu create_async die Funktion übergeben.Diese Variationen erzeugen ein anderes Verhalten.Wenn Sie einen Wert zurückgeben, wird die Arbeitsfunktion in task umschlossen, damit sie auf einen Hintergrundthread ausgeführt werden kann.Außerdem verwendet zugrunde liegende task ein implizites Abbruchtoken.Wenn Sie ein task-Objekt zurückgeben, wird die Arbeitsfunktion synchron ausgeführt.Wenn Sie ein task-Objekt zurückgeben, stellen Sie sicher, dass alle längeren Vorgänge in der Arbeitsfunktion auch als Aufgaben, die App ermöglichen, bleibt reaktionsfähig ausgeführt werden.Außerdem verwendet zugrunde liegende task kein implizites Abbruchtoken.Daher müssen Sie die Arbeitsfunktion definieren, um ein cancellation_token-Objekt zu verwenden, wenn Sie Unterstützung für Abbrüche anfordern, wenn Sie ein task-Objekt von create_async zurückgeben.
Das folgende Beispiel zeigt die verschiedenen Möglichkeiten, ein IAsyncAction-Objekt zu erstellen, das von einer anderen Komponente Windows-Runtime genutzt werden kann.
// 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.
});
});
Anfang[]
Beispiel: Das Windows Runtime-Komponente und Nutzen einer C++-Datei sie über C# erstellen
Betrachten Sie eine Anwendung, die XAML und C# verwendet, um die Benutzeroberfläche- und Wechselstrom++ Windows-Runtime Komponente zu definieren, um COMPUTE-intensive Vorgänge auszuführen.In diesem Beispiel sind die C++-Komponentenberechnungen, das in einem angegebenen Bereich nummeriert, um Primzahlen.Um die Unterschiede zwischen den vier asynchronen Aufgabenschnittstellen Windows-Runtime zu veranschaulichen, beginnen Sie, in Visual Studio, indem Sie Leere Projektmappe erstellen und es Bereitet vor benennen.Fügen Sie dann der Projektmappe ein Komponente für Windows-Runtime Projekt und das Benennen es PrimesLibrary hinzu.Fügen Sie den folgenden Code der generierten C++-Headerdatei hinzu (in diesem Beispiel umbenannt wird Class1.h zu Primes.h).Jede public-Methode definiert eine der vier asynchronen Schnittstellen.Die Methoden, die eine Wertsrückgabe Windows::Foundation::Collections::IVector<int> ein Objekt zurückgeben.Die Methoden, die den Status melden, erzeugen double-Werte, die den Prozentsatz der Gesamtarbeit definieren, die abgeschlossen ist.
#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);
};
}
Hinweis |
---|
Gemäß der Konvention beenden asynchrone Methodennamen in Windows-Runtime normalerweise mit asynchronem "". |
Fügen Sie den folgenden Code der generierten C++-Quelldatei hinzu (in diesem Beispiel umbenannt wird Class1.cpp zu Primes.cpp).Die is_prime-Funktion bestimmt, ob die Eingabe eine Primzahl ist.Die übrigen Methoden implementieren die Primes-Klasse.Jeder Aufruf von create_async verwendet eine Signatur, die mit der - Methode kompatibel ist, der aufgerufen wird.Da beispielsweise Primes::ComputePrimesAsyncIAsyncAction zurückgibt, gibt die Arbeitsfunktion, die zu create_async bereitgestellt wird, keinen Wert zurück und nimmt ein progress_reporter-Objekt nicht als Parameter.
// 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;
});
}
Jede Methode führt zuerst die Validierung durch, um sicherzugehen, dass die Eingabeparameter nicht negativ sind.Wenn ein Eingabewert negativ ist, die Methodenwürfe Platform::InvalidArgumentException.Fehlerbehandlung wird weiter unten in diesem Abschnitt beschrieben.
Um diese Methoden aus einer Windows Store-App zu nutzen, verwenden Sie die Vorlage Visual C# Leere App (XAML) um ein zweites Projekt der Visual Studio-Projektmappe hinzuzufügen.In diesem Beispiel heißt das Projekt Bereitet vor.Anschließend Bereitet vor vom Projekt, fügen Sie einen Verweis auf PrimesLibrary Projekt hinzu.
Fügen Sie den folgenden Code "MainPage.xaml" hinzu.Dieser Code definiert die Benutzeroberfläche, damit Sie die C++-Komponente aufrufen können und Anzeige entsteht.
<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>
Fügen Sie den folgenden Code der - Klasse MainPage in "MainPage.xaml" hinzu.Dieser Code definiert ein - Objekt und die Primes Schaltflächenereignishandler.
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();
}
Diese Methoden verwenden die async und await Schlüsselwörter, um die Benutzeroberfläche zu aktualisieren nach der vollständigen asynchronen Vorgänge.Informationen über die asynchrone Muster, die für C# und in Visual Basic verfügbar sind, finden Sie unter Asynchrone Muster in Windows Store-Apps mit C# und Asynchrone Muster in Windows Store-Apps mit VB.
Die getPrimesCancellation und cancelGetPrimes-Methoden arbeiten zusammen, um dem Benutzer zu ermöglichen, die Operation abzubrechen.Wenn der Benutzer die Schaltfläche Abbrechen auswählt, die cancelGetPrimes-Methodenaufrufe IAsyncOperationWithProgress<TResult, TProgress>::Cancel, um den Vorgang abzubrechen.Die Concurrency Runtime, die den zugrunde liegenden asynchronen Vorgang verwaltet, löst einen internen Ausnahmetyp aus, der von Windows-Runtime abgefangen wird, um zu kommunizieren, dass Abbruch abgeschlossen ist.Weitere Informationen über das Abbruchmodell, finden Sie unter Abbruch in der PPL.
Wichtig |
---|
Um die Concurrency Runtime zu ermöglichen Windows-Runtime ordnungsgemäß zu melden dass es den Vorgang abgebrochen hat, fangen Sie diesen internen Ausnahmetyp ab.Dies bedeutet, dass Sie alle Ausnahmen (catch (...)) auch auffangen sollen.Wenn Sie alle Ausnahmen abfangen müssen, lösen Sie die Ausnahme erneut, um sicherzustellen, dass Windows-Runtime den Erstellung des - Objekts abgeschlossen werden kann. |
Die folgende Abbildung zeigt die Bereitet vor App an, wenn eine Option ausgewählt wurde.
Beispiele, die create_async verwenden, um asynchrone Aufgaben erstellen, die von anderen Sprachen genutzt werden können, finden Sie unter Die Verwendung der im Maps-Reise-Optimiererbeispiel Bing und Windows 8 asynchrone Vorgänge in C++ mit PPL.
Anfang[]
Steuern des Ausführungs-Threads
Windows-Runtime verwendet das COM-Threadingmodell.In diesem Modell werden Objekte in unterschiedlichen Apartments gehostet, je nachdem, wie sie ihre Synchronisierung behandeln.Threadsichere Objekte werden im Multithread-Apartment (MTA) gehostet.Objekte, auf die von einem einzelnen zugegriffen werden müssen, werden in einem Singlethread-Apartment (STA) gehostet.
In einer Anwendung, die über eine Benutzeroberfläche verfügt, ist der Thread ASTA Anwendung (STA) für pumpende Fenstermeldungen zuständig und ist der einzige Thread im Prozess, der die STA-gehosteten UI-Steuerelemente aktualisieren kann.Dies hat zwei Folgen.Zuerst die Anwendung ermöglichen, bleibt reaktionsfähig, sollten alle CPU-intensive E/A-Vorgänge und nicht auf den ASTA-Thread ausgeführt werden.Zweitens müssen Ergebnisse, die von den Hintergrundthreads stammen, zurück zum ASTA gemarshallt werden, um die Benutzeroberfläche zu aktualisieren.In App in C++ Windows Store Seiten MainPage und andere XAML ähnlich Ausführung auf dem ATSA.Daher können Aufgabenfortsetzungen, die auf dem ASTA ausgeführt werden dort standardmäßig deklariert werden, sodass Sie Steuerelemente direkt im Fortsetzungstext aktualisieren.Wenn Sie eine Aufgabe in einer anderen Aufgabe schachteln, durchlaufen alle Fortsetzungen auf der geschachtelte Aufgabe in das MTA.Daher müssen Sie prüfen, ob explizit angibt, auf der Kontext Ausführung dieser Fortsetzungen.
Eine Aufgabe, der aus einem asynchronen Operation, z IAsyncOperation<TResult>, besondere Semantik der dem erstellt wird, die Ihnen helfen kann, das Threading zu ignorieren, führt aufgeführt.Obwohl ein Vorgang kann auf einen Hintergrundthread (oder ihn kann nicht durch einen Thread jemals unterstützt werden), ausgeführt wird, seine Fortsetzungen werden standardmäßig sichergestellt, dass auf das Apartment ausgeführt, das die Fortsetzungsvorgänge gestartet hat (das heißt, vom Apartment, das task::then hat).Sie können die concurrency::task_continuation_context-Klasse verwenden, um den Ausführungskontext einer Fortsetzung zu steuern.Verwenden Sie diese statischen Hilfsmethoden, um task_continuation_context-Objekte zu erstellen:
Verwendung concurrency::task_continuation_context::use_arbitrary, anzugeben, dass die Fortsetzung in einem Hintergrundthread ausgeführt wird.
Verwendung concurrency::task_continuation_context::use_current, anzugeben, dass die Fortsetzung auf den Thread ausgeführt wird, der task::then aufgerufen wurde.
Sie können ein task_continuation_context-Objekt zur task::then-Methode übergeben, um den Ausführungskontext der Fortsetzung explizit zu steuern, oder Sie können die Aufgabe einem anderen Apartment übergeben und die task::then-Methode dann aufrufen, um den Ausführungskontext implizit zu steuern.
Wichtig |
---|
Da der primäre UI-Thread von Windows Store-App mit STA ausgeführt werden, werden Fortsetzungen, die Sie auf diesem STA standardmäßig erstellen, auf das STA ausgeführt.Entsprechend werden Fortsetzungen, die auf dem MTA erstellen, auf das MTA ausgeführt. |
Der folgende Abschnitt zeigt eine Anwendung, die eine Datei vom Datenträger gelesen wird, durchsucht die häufigsten Wörter in dieser Datei und die Anzeige der Ergebnisse im Benutzeroberfläche an.Der letzte Vorgang, die Benutzeroberfläche Aktualisieren, tritt im UI-Thread auf.
Wichtig |
---|
Dieses Verhalten ist zu Windows Store-App bestimmt.Für Desktop-Apps steuern Sie nicht an Fortsetzungsausführung.Stattdessen wählt der Planer einen Arbeitsthread aus, in dem jede Fortsetzung ausgeführt wird. |
Wichtig |
---|
Rufen Sie nicht concurrency::task::wait im Text einer Fortsetzung auf, die auf das STA ausgeführt wird.Andernfalls löst die Laufzeit concurrency::invalid_operation aus, da diese Methode den aktuellen Thread blockiert und die Anwendung bewirken kann, nicht mehr reagiert.Sie können jedoch die concurrency::task::get-Methode aufrufen, um das Ergebnis der vorherigen Aufgabe in einer aufgabenbasierte Fortsetzung zu empfangen. |
Anfang[]
Beispiel: Steuern der Ausführung in einer Windows Store-App mit C++ und XAML
Betrachten Sie App einer C++-Datei XAML, die eine Datei vom Datenträger gelesen wird, durchsucht die häufigsten Wörter in dieser Datei anzeigt und dann die Ergebnisse in der Benutzeroberfläche.Um diese App zu erstellen, starten Sie, in Visual Studio, indem Sie ein Projekt Windows StoreLeere App (XAML) erstellen und es CommonWords benennen.In dem Anwendungsmanifest geben Sie die Funktion Dokumentbibliothek an, die Anwendung zu aktivieren, um auf den Ordner zuzugreifen.Fügen Sie auch den Dateityp des Texts (.txt) dem Deklarationsabschnitt des Anwendungsmanifests hinzu.Weitere Informationen zu App-Funktionen und -Deklarationen, finden Sie unter Anwendungspakete und Bereitstellung.
Aktualisieren Sie das Element in Grid MainPage.xaml, um ein ProgressRing-Element und ein Element TextBlock einzuschließen.ProgressRing gibt an, dass der Vorgang ausgeführt und die TextBlock zeigt die Ergebnisse der Berechnung ist.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ProgressRing x:Name="Progress"/>
<TextBlock x:Name="Results" FontSize="16"/>
</Grid>
Fügen Sie die folgenden Anweisungen #include pch.h hinzu.
#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>
Fügen Sie die folgenden Methodendeklarationen der MainPage-Klasse hinzufügen (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);
Fügen Sie die folgenden Anweisungen using MainPage.cpp hinzu.
using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
In MainPage.cpp implementieren Sie MainPage::MakeWordList, MainPage::FindCommonWords und MainPage::ShowResults-Methoden.MainPage::MakeWordList und MainPage::FindCommonWords führen Computer-intensive Vorgängen.Die MainPage::ShowResults-Methode zeigt das Ergebnis der Berechnung im Benutzeroberfläche an.
// 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());
}
Ändern Sie den MainPage-Konstruktor, um eine Kette von Fortsetzungsaufgaben zu erstellen, die in der Benutzeroberfläche angezeigt, das die allgemeinen, im Buch die Ilias von Homer durchführt.Die ersten beiden Fortsetzungsaufgaben, die den Text in einzelne Wörter und allgemeine Wörter in der Suche teilen, können zeitaufwendig sein und werden daher explizit festgelegt, um im Hintergrund ausgeführt werden.Die letzte Fortsetzungsaufgabe, die die Benutzeroberfläche aktualisiert, gibt keinen Fortsetzungskontext an und folgt daher den Apartmentthreadingregeln.
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.
});
}
Hinweis |
---|
Dieses Beispiel zeigt, wie Ausführungskontexte angegeben und wie eine Kette von Fortsetzungen zusammengesetzt.Denken Sie dass standardmäßig eine Aufgabe, die aus einem asynchronen Vorgang erstellt wird, seine Fortsetzungen auf dem Apartment ausführt, das task::then aufgerufen wurde.Daher werden in diesem Beispiel task_continuation_context::use_arbitrary, um anzugeben, dass Vorgänge, die nicht die Benutzeroberfläche umfassen, in einem Hintergrundthread ausgeführt werden. |
Die folgende Abbildung zeigt die Ergebnisse der CommonWords App an.
In diesem Beispiel ist es möglich, Abbruch zu unterstützen, da task-Objekte, die create_async unterstützen, ein implizites Abbruchtoken verwenden.Definieren Sie die Arbeitsfunktion, um ein cancellation_token-Objekt akzeptiert, wenn die Aufgaben auf Abbrüche in kooperative Weise reagieren müssen.Weitere Informationen zum Abbruch in der PPL, finden Sie unter Abbruch in der PPL
Anfang[]