Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym dokumencie opisano niektóre kluczowe kwestie, które należy wziąć pod uwagę podczas używania klasy zadań do tworzenia operacji asynchronicznych opartych na puli wątków systemu Windows w aplikacji uniwersalnej środowisko wykonawcze systemu Windows (UWP).
Użycie programowania asynchronicznego jest kluczowym składnikiem w modelu aplikacji środowisko wykonawcze systemu Windows, ponieważ umożliwia aplikacjom reagowanie na dane wejściowe użytkownika. Długotrwałe zadanie można uruchomić bez blokowania wątku interfejsu użytkownika i później można uzyskać wyniki zadania. Możesz również anulować zadania i otrzymywać powiadomienia o postępie, gdy zadania są uruchamiane w tle. Dokument Programowanie asynchroniczne w języku C++ zawiera omówienie asynchronicznego wzorca dostępnego w języku Visual C++ w celu tworzenia aplikacji platformy UWP. W tym dokumencie pokazano, jak używać i tworzyć łańcuchy operacji asynchronicznych środowisko wykonawcze systemu Windows. W tej sekcji opisano sposób używania typów w pliku ppltasks.h do tworzenia operacji asynchronicznych, które mogą być używane przez inny składnik środowisko wykonawcze systemu Windows i jak kontrolować sposób wykonywania pracy asynchronicznej. Rozważ również przeczytanie wzorców programowania asynchronicznego i wskazówek w witrynie Hilo (aplikacje ze Sklepu Windows przy użyciu języka C++ i XAML), aby dowiedzieć się, jak używaliśmy klasy zadań do implementowania operacji asynchronicznych w hilo, aplikacji środowisko wykonawcze systemu Windows przy użyciu języka C++ i XAML.
Uwaga
Bibliotekę równoległych wzorców (PPL) i bibliotekę agentów asynchronicznych można używać w aplikacji platformy UWP. Nie można jednak użyć harmonogramu zadań ani menedżera zasobów. W tym dokumencie opisano dodatkowe funkcje udostępniane przez PPL dostępne tylko dla aplikacji platformy UWP, a nie dla aplikacji klasycznej.
Kwestie kluczowe
Użyj współbieżności::create_async , aby utworzyć operacje asynchroniczne, które mogą być używane przez inne składniki (które mogą być zapisywane w językach innych niż C++).
Użyj współbieżności::p rogress_reporter , aby zgłosić powiadomienia o postępie do składników wywołujących operacje asynchroniczne.
Użyj tokenów anulowania, aby włączyć wewnętrzne operacje asynchroniczne do anulowania.
Zachowanie
create_async
funkcji zależy od zwracanego typu funkcji pracy, która jest do niej przekazywana. Funkcja pracy zwracająca zadanie (task<T>
lubtask<void>
) jest uruchamiana synchronicznie w kontekście o nazwiecreate_async
. Funkcja pracy zwracającaT
lubvoid
uruchamiana w dowolnym kontekście.Możesz użyć metody concurrency::task::then , aby utworzyć łańcuch zadań, które są uruchamiane po drugiej. W aplikacji platformy UWP domyślny kontekst kontynuacji zadania zależy od sposobu konstruowania tego zadania. Jeśli zadanie zostało utworzone przez przekazanie akcji asynchronicznej do konstruktora zadania lub przekazanie wyrażenia lambda zwracającego akcję asynchroniczną, domyślny kontekst dla wszystkich kontynuacji tego zadania jest bieżącym kontekstem. Jeśli zadanie nie jest konstruowane z akcji asynchronicznej, domyślnie do kontynuacji zadania jest używany dowolny kontekst. Domyślny kontekst można zastąpić za pomocą klasy concurrency::task_continuation_context .
W tym dokumencie
Tworzenie operacji asynchronicznych
Możesz użyć modelu zadań i kontynuacji w bibliotece wzorców równoległych (PPL), aby zdefiniować zadania w tle, a także dodatkowe zadania uruchamiane po zakończeniu poprzedniego zadania. Ta funkcja jest udostępniana przez klasę concurrency::task . Aby uzyskać więcej informacji na temat tego modelu i task
klasy, zobacz Równoległość zadań.
Środowisko wykonawcze systemu Windows to interfejs programowania, którego można użyć do tworzenia aplikacji platformy UNIWERSALNEJ systemu Windows, które działają tylko w specjalnym środowisku systemu operacyjnego. Takie aplikacje używają autoryzowanych funkcji, typów danych i urządzeń oraz są dystrybuowane ze Sklepu Microsoft. Środowisko wykonawcze systemu Windows jest reprezentowana przez interfejs binarny aplikacji (ABI). ABI to podstawowy kontrakt binarny, który udostępnia interfejsy API środowisko wykonawcze systemu Windows dla języków programowania, takich jak Visual C++.
Korzystając z środowisko wykonawcze systemu Windows, możesz użyć najlepszych funkcji różnych języków programowania i połączyć je w jedną aplikację. Możesz na przykład utworzyć interfejs użytkownika w języku JavaScript i wykonać logikę aplikacji intensywnie korzystającej z obliczeń w składniku języka C++. Możliwość wykonywania tych operacji intensywnie korzystających z obliczeń w tle jest kluczowym czynnikiem w utrzymaniu reakcji interfejsu użytkownika. task
Ponieważ klasa jest specyficzna dla języka C++, należy użyć interfejsu środowisko wykonawcze systemu Windows do komunikowania operacji asynchronicznych z innymi składnikami (które mogą być napisane w językach innych niż C++). Środowisko wykonawcze systemu Windows udostępnia cztery interfejsy, których można użyć do reprezentowania operacji asynchronicznych:
Windows::Foundation::IAsyncAction
Reprezentuje akcję asynchroniczną.
Windows::Foundation::IAsyncActionWithProgress TProgress<>
Reprezentuje akcję asynchroniczną, która zgłasza postęp.
Windows::Foundation::IAsyncOperation<TResult>
Reprezentuje operację asynchroniczną, która zwraca wynik.
Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
Reprezentuje operację asynchroniczną, która zwraca wynik i zgłasza postęp.
Pojęcie akcji oznacza, że zadanie asynchroniczne nie generuje wartości (pomyśl o funkcji zwracającej void
wartość ). Pojęcie operacji oznacza, że zadanie asynchroniczne generuje wartość. Pojęcie postępu oznacza, że zadanie może zgłaszać komunikaty postępu do obiektu wywołującego. Języki JavaScript, .NET Framework i Visual C++ zapewniają własny sposób tworzenia wystąpień tych interfejsów do użycia w granicach usługi ABI. W przypadku języka Visual C++PPL udostępnia funkcję concurrency::create_async . Ta funkcja tworzy środowisko wykonawcze systemu Windows asynchroniczną akcję lub operację reprezentującą ukończenie zadania. Funkcja create_async
przyjmuje funkcję pracy (zazwyczaj wyrażenie lambda), wewnętrznie tworzy task
obiekt i opakowuje to zadanie w jednym z czterech asynchronicznych interfejsów środowisko wykonawcze systemu Windows.
Uwaga
Użyj create_async
tylko wtedy, gdy musisz utworzyć funkcje, do których można uzyskać dostęp z innego języka lub innego składnika środowisko wykonawcze systemu Windows. task
Użyj klasy bezpośrednio, gdy wiadomo, że operacja jest zarówno utworzona, jak i użyta przez kod języka C++ w tym samym składniku.
Zwracany create_async
typ klasy jest określany przez typ argumentów. Jeśli na przykład funkcja pracy nie zwraca wartości i nie zgłasza postępu, create_async
zwraca wartość IAsyncAction
. Jeśli funkcja pracy nie zwraca wartości, a także zgłasza postęp, create_async
zwraca wartość IAsyncActionWithProgress
. Aby zgłosić postęp, podaj obiekt concurrency::p rogress_reporter jako parametr funkcji pracy. Możliwość raportowania postępu umożliwia raportowanie ilości wykonanej pracy i ilość nadal pozostaje (na przykład jako wartość procentowa). Umożliwia również raportowanie wyników w miarę ich dostępności.
Interfejsy IAsyncAction
, IAsyncActionWithProgress<TProgress>
, IAsyncOperation<TResult>
i IAsyncActionOperationWithProgress<TProgress, TProgress>
zapewniają metodę umożliwiającą Cancel
anulowanie operacji asynchronicznej. Klasa task
działa z tokenami anulowania. Jeśli używasz tokenu anulowania do anulowania pracy, środowisko uruchomieniowe nie uruchamia nowej pracy, która subskrybuje ten token. Praca, która jest już aktywna, może monitorować token anulowania i zatrzymywać, kiedy to możliwe. Ten mechanizm został szczegółowo opisany w dokumencie Anulowanie w PPL. Anulowanie zadania można połączyć za pomocą metod środowisko wykonawcze systemu Windows Cancel
na dwa sposoby. Najpierw można zdefiniować funkcję pracy przekazywaną do create_async
wykonywania współbieżności::cancellation_token obiektu. Po wywołaniu Cancel
metody ten token anulowania jest anulowany, a normalne reguły anulowania mają zastosowanie do bazowego task
obiektu obsługującego create_async
wywołanie. Jeśli nie podasz cancellation_token
obiektu, obiekt źródłowy task
definiuje go niejawnie. Zdefiniuj cancellation_token
obiekt, gdy musisz wspólnie reagować na anulowanie w funkcji pracy. Przykład sekcji: Kontrolowanie wykonywania w aplikacji środowisko wykonawcze systemu Windows przy użyciu języka C++ i XAML przedstawia przykład sposobu przeprowadzania anulowania w aplikacji platforma uniwersalna systemu Windows (UWP) przy użyciu języka C# i XAML korzystającego z niestandardowego kodu środowisko wykonawcze systemu Windows składnik języka C++.
Ostrzeżenie
W łańcuchu kontynuacji zadań zawsze należy wyczyścić stan, a następnie wywołać współbieżność::cancel_current_task po anulowaniu tokenu anulowania. Jeśli zwracasz wcześniej zamiast wywoływać cancel_current_task
metodę , operacja przechodzi do stanu ukończonego zamiast stanu anulowanego.
W poniższej tabeli podsumowano kombinacje, których można użyć do zdefiniowania operacji asynchronicznych w aplikacji.
Aby utworzyć ten interfejs środowisko wykonawcze systemu Windows | Zwróć ten typ z create_async |
Przekaż te typy parametrów do funkcji roboczej, aby użyć niejawnego tokenu anulowania | Przekaż te typy parametrów do funkcji roboczej, aby użyć jawnego tokenu anulowania |
---|---|---|---|
IAsyncAction |
void lub task<void> |
(brak) | (cancellation_token ) |
IAsyncActionWithProgress<TProgress> |
void lub task<void> |
(progress_reporter ) |
(progress_reporter , cancellation_token ) |
IAsyncOperation<TResult> |
T lub task<T> |
(brak) | (cancellation_token ) |
IAsyncActionOperationWithProgress<TProgress, TProgress> |
T lub task<T> |
(progress_reporter ) |
(progress_reporter , cancellation_token ) |
Wartość lub task
obiekt można zwrócić z funkcji roboczej przekazanej create_async
do funkcji . Te odmiany generują różne zachowania. Gdy zwracasz wartość, funkcja pracy jest owinięta w obiekcie task
, aby można było ją uruchomić w wątku w tle. Ponadto bazowe task
używa niejawnego tokenu anulowania. Z drugiej strony, jeśli zwracasz task
obiekt, funkcja robocza jest uruchamiana synchronicznie. W związku z tym, jeśli zwracasz task
obiekt, upewnij się, że wszystkie długie operacje w funkcji roboczej są również uruchamiane jako zadania, aby umożliwić aplikacji zachowanie reakcji. Ponadto bazowe task
nie używa niejawnego tokenu anulowania. W związku z tym należy zdefiniować funkcję roboczą cancellation_token
, aby podjąć obiekt, jeśli potrzebujesz obsługi anulowania podczas zwracania task
obiektu z create_async
obiektu .
W poniższym przykładzie przedstawiono różne sposoby tworzenia IAsyncAction
obiektu, który może być używany przez inny składnik środowisko wykonawcze systemu Windows.
// 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.
});
});
Przykład: tworzenie składnika środowisko wykonawcze systemu Windows języka C++ i korzystanie z niego z języka C#
Rozważmy aplikację, która używa języka XAML i C# do definiowania interfejsu użytkownika i składnika środowisko wykonawcze systemu Windows języka C++ w celu wykonywania operacji intensywnie korzystających z obliczeń. W tym przykładzie składnik języka C++ oblicza liczby w danym zakresie jako podstawowe. Aby zilustrować różnice między czterema interfejsami zadań asynchronicznych środowisko wykonawcze systemu Windows, uruchom polecenie w programie Visual Studio, tworząc puste rozwiązanie i nadając mu Primes
nazwę . Następnie dodaj do rozwiązania projekt środowisko wykonawcze systemu Windows Component i nadaj mu PrimesLibrary
nazwę . Dodaj następujący kod do wygenerowanego pliku nagłówka języka C++ (w tym przykładzie zmieniono nazwę klasy Class1.h na Primes.h). Każda public
metoda definiuje jeden z czterech interfejsów asynchronicznych. Metody zwracające wartość zwracają obiekt Windows::Foundation::Collections::IVector<int> . Metody, które zgłaszają postęp, generują double
wartości, które definiują procent ogólnej pracy, która została ukończona.
#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);
};
}
Uwaga
Zgodnie z konwencją nazwy metod asynchronicznych w środowisko wykonawcze systemu Windows zwykle kończą się ciągiem "Async".
Dodaj następujący kod do wygenerowanego pliku źródłowego języka C++ (w tym przykładzie zmieniono nazwę Class1.cpp na Primes.cpp). Funkcja is_prime
określa, czy jego dane wejściowe są prime. Pozostałe metody implementują klasę Primes
. Każde wywołanie używa create_async
podpisu zgodnego z metodą, z której jest wywoływana. Na przykład, ponieważ Primes::ComputePrimesAsync
zwraca IAsyncAction
wartość , funkcja pracy podana do create_async
polecenia nie zwraca wartości i nie pobiera progress_reporter
obiektu jako parametru.
// PrimesLibrary.cpp
#include "pch.h"
#include "Primes.h"
#include <atomic>
#include <collection.h>
#include <ppltasks.h>
#include <concurrent_vector.h>
using namespace concurrency;
using namespace std;
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace PrimesLibrary;
Primes::Primes()
{
}
// Determines whether the input value is prime.
bool is_prime(int n)
{
if (n < 2)
{
return false;
}
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
{
return false;
}
}
return true;
}
// Adds the numbers that are prime in the provided range
// to the primes global variable.
IAsyncAction^ Primes::ComputePrimesAsync(int first, int last)
{
return create_async([this, first, last]
{
// Ensure that the input values are in range.
if (first < 0 || last < 0)
{
throw ref new InvalidArgumentException();
}
// Perform the computation in parallel.
parallel_for(first, last + 1, [this](int n)
{
if (is_prime(n))
{
// Perhaps store the value somewhere...
}
});
});
}
IAsyncActionWithProgress<double>^ Primes::ComputePrimesWithProgressAsync(int first, int last)
{
return create_async([first, last](progress_reporter<double> reporter)
{
// Ensure that the input values are in range.
if (first < 0 || last < 0)
{
throw ref new InvalidArgumentException();
}
// Perform the computation in parallel.
atomic<long> operation = 0;
long range = last - first + 1;
double lastPercent = 0.0;
parallel_for(first, last + 1, [&operation, range, &lastPercent, reporter](int n)
{
// Report progress message.
double progress = 100.0 * (++operation) / range;
if (progress >= lastPercent)
{
reporter.report(progress);
lastPercent += 1.0;
}
if (is_prime(n))
{
// Perhaps store the value somewhere...
}
});
reporter.report(100.0);
});
}
IAsyncOperation<IVector<int>^>^ Primes::GetPrimesAsync(int first, int last)
{
return create_async([this, first, last]() -> IVector<int>^
{
// Ensure that the input values are in range.
if (first < 0 || last < 0)
{
throw ref new InvalidArgumentException();
}
// Perform the computation in parallel.
concurrent_vector<int> primes;
parallel_for(first, last + 1, [this, &primes](int n)
{
// If the value is prime, add it to the global vector.
if (is_prime(n))
{
primes.push_back(n);
}
});
// Sort the results.
sort(begin(primes), end(primes), less<int>());
// Copy the results to an IVector object. The IVector
// interface makes collections of data available to other
// Windows Runtime components.
auto results = ref new Vector<int>();
for (int prime : primes)
{
results->Append(prime);
}
return results;
});
}
IAsyncOperationWithProgress<IVector<int>^, double>^ Primes::GetPrimesWithProgressAsync(int first, int last)
{
return create_async([this, first, last](progress_reporter<double> reporter) -> IVector<int>^
{
// Ensure that the input values are in range.
if (first < 0 || last < 0)
{
throw ref new InvalidArgumentException();
}
// Perform the computation in parallel.
concurrent_vector<int> primes;
long operation = 0;
long range = last - first + 1;
double lastPercent = 0.0;
parallel_for(first, last + 1, [&primes, &operation, range, &lastPercent, reporter](int n)
{
// Report progress message.
double progress = 100.0 * (++operation) / range;
if (progress >= lastPercent)
{
reporter.report(progress);
lastPercent += 1.0;
}
// If the value is prime, add it to the local vector.
if (is_prime(n))
{
primes.push_back(n);
}
});
reporter.report(100.0);
// Sort the results.
sort(begin(primes), end(primes), less<int>());
// Copy the results to an IVector object. The IVector
// interface makes collections of data available to other
// Windows Runtime components.
auto results = ref new Vector<int>();
for (int prime : primes)
{
results->Append(prime);
}
return results;
});
}
Każda metoda najpierw przeprowadza walidację, aby upewnić się, że parametry wejściowe nie są ujemne. Jeśli wartość wejściowa jest ujemna, metoda zgłasza wyjątek Platform::InvalidArgumentException. Obsługa błędów wyjaśniono w dalszej części tej sekcji.
Aby korzystać z tych metod z aplikacji platformy UWP, użyj szablonu Pusta aplikacja języka Visual C# (XAML), aby dodać drugi projekt do rozwiązania programu Visual Studio. W tym przykładzie nazwa projektu Primes
. Następnie z Primes
projektu dodaj odwołanie do PrimesLibrary
projektu.
Dodaj następujący kod do pliku MainPage.xaml. Ten kod definiuje interfejs użytkownika, aby można było wywołać składnik C++ i wyświetlić wyniki.
<Page
x:Class="Primes.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Primes"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://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>
Dodaj następujący kod do klasy w pliku MainPage
MainPage.xaml. Ten kod definiuje Primes
obiekt i programy obsługi zdarzeń przycisku.
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();
}
Te metody używają async
słów kluczowych i await
, aby zaktualizować interfejs użytkownika po zakończeniu operacji asynchronicznych. Aby uzyskać informacje na temat asynchronicznego kodowania w aplikacjach platformy UWP, zobacz Wątkowanie i programowanie asynchroniczne.
Metody getPrimesCancellation
i cancelGetPrimes
współpracują ze sobą, aby umożliwić użytkownikowi anulowanie operacji. Gdy użytkownik wybierze przycisk Anuluj, metoda wywołuje metodę IAsyncOperationWithProgress<TResult, TProgress>::Anuluj, aby anulować cancelGetPrimes
operację. Środowisko uruchomieniowe współbieżności, które zarządza podstawową operacją asynchroniczną, zgłasza wewnętrzny typ wyjątku przechwycony przez środowisko wykonawcze systemu Windows w celu przekazania, że anulowanie zostało zakończone. Aby uzyskać więcej informacji na temat modelu anulowania, zobacz Anulowanie.
Ważne
Aby umożliwić użytkownikowi PPL poprawne raportowanie do środowisko wykonawcze systemu Windows, że operacja została anulowana, nie przechwyć tego typu wyjątku wewnętrznego. Oznacza to, że nie należy również przechwytywać wszystkich wyjątków (catch (...)
). Jeśli musisz przechwycić wszystkie wyjątki, wróć ponownie wyjątek, aby upewnić się, że środowisko wykonawcze systemu Windows może zakończyć operację anulowania.
Poniższa ilustracja przedstawia aplikację po wybraniu Primes
każdej opcji.
Aby zapoznać się z przykładem używanym create_async
do tworzenia zadań asynchronicznych, które mogą być używane przez inne języki, zobacz Używanie języka C++ w przykładzie Bing Maps Trip Optimizer.
Kontrolowanie wątku wykonywania
W środowisko wykonawcze systemu Windows jest używany model wątkowania COM. W tym modelu obiekty są hostowane w różnych mieszkaniach, w zależności od sposobu ich obsługi synchronizacji. Obiekty bezpieczne wątkowo są hostowane w mieszkaniu wielowątkowy (MTA). Obiekty, do których musi uzyskiwać dostęp pojedynczy wątek, są hostowane w mieszkaniu jednowątkowym (STA).
W aplikacji, która ma interfejs użytkownika, wątek ASTA (Application STA) jest odpowiedzialny za komunikaty okna pompowania i jest jedynym wątkiem w procesie, który może zaktualizować kontrolki interfejsu użytkownika hostowanego przez sta. Ma to dwie konsekwencje. Najpierw, aby umożliwić aplikacji zachowanie reakcji, wszystkie operacje intensywnie korzystające z procesora CPU i we/wy nie powinny być uruchamiane w wątku ASTA. Po drugie, wyniki pochodzące z wątków w tle muszą być marshalowane z powrotem do usługi ASTA, aby zaktualizować interfejs użytkownika. W aplikacji MainPage
platformy UWP języka C++ i innych stronach XAML wszystkie są uruchamiane w usłudze ATSA. W związku z tym kontynuacje zadań zadeklarowane na platformie ASTA są uruchamiane domyślnie, dzięki czemu można aktualizować kontrolki bezpośrednio w treści kontynuacji. Jeśli jednak zagnieżdżasz zadanie w innym zadaniu, wszelkie kontynuacje tego zagnieżdżonego zadania są uruchamiane w usłudze MTA. W związku z tym należy rozważyć, czy jawnie określić kontekst, w którym są uruchamiane te kontynuacje.
Zadanie utworzone na podstawie operacji asynchronicznej, takiej jak IAsyncOperation<TResult>
, używa specjalnej semantyki, które mogą pomóc w ignorowaniu szczegółów wątków. Mimo że operacja może być uruchamiana na wątku w tle (lub nie może być w ogóle wspierana przez wątek), jego kontynuacje są domyślnie gwarantowane do uruchomienia w mieszkaniu, które rozpoczęło operacje kontynuacji (innymi słowy, z mieszkania, które nazywało task::then
). Możesz użyć klasy concurrency::task_continuation_context , aby kontrolować kontekst wykonywania kontynuacji. Użyj tych statycznych metod pomocnika, aby utworzyć task_continuation_context
obiekty:
Użyj współbieżności::task_continuation_context::use_arbitrary , aby określić, że kontynuacja jest uruchamiana w wątku w tle.
Użyj współbieżności::task_continuation_context::use_current , aby określić, że kontynuacja jest uruchamiana w wątku o nazwie
task::then
.
Obiekt można przekazać task_continuation_context
do metody task::then , aby jawnie kontrolować kontekst wykonywania kontynuacji lub przekazać zadanie do innego mieszkania, a następnie wywołać task::then
metodę, aby niejawnie kontrolować kontekst wykonywania.
Ważne
Ponieważ główny wątek interfejsu użytkownika aplikacji platformy UWP działa w obszarze STA, kontynuacje tworzone na tym sta domyślnie są uruchamiane w sta. W związku z tym kontynuacje tworzone w usłudze MTA są uruchamiane w usłudze MTA.
W poniższej sekcji przedstawiono aplikację, która odczytuje plik z dysku, znajduje najbardziej typowe słowa w tym pliku, a następnie wyświetla wyniki w interfejsie użytkownika. Ostateczna operacja, aktualizowanie interfejsu użytkownika, odbywa się w wątku interfejsu użytkownika.
Ważne
To zachowanie jest specyficzne dla aplikacji platformy UWP. W przypadku aplikacji klasycznych nie kontrolujesz miejsca uruchamiania kontynuacji. Zamiast tego harmonogram wybiera wątek procesu roboczego, na którym ma być uruchamiana każda kontynuacja.
Ważne
Nie należy wywoływać współbieżności::task::wait w treści kontynuacji uruchamianej w stacie. W przeciwnym razie środowisko uruchomieniowe zgłasza współbieżność::invalid_operation , ponieważ ta metoda blokuje bieżący wątek i może spowodować, że aplikacja przestanie odpowiadać. Można jednak wywołać metodę concurrency::task::get , aby otrzymać wynik zadania przedzibowego w kontynuacji opartej na zadaniach.
Przykład: kontrolowanie wykonywania w aplikacji środowisko wykonawcze systemu Windows przy użyciu języka C++ i XAML
Rozważmy aplikację XAML języka C++, która odczytuje plik z dysku, znajduje najbardziej typowe słowa w tym pliku, a następnie wyświetla wyniki w interfejsie użytkownika. Aby utworzyć tę aplikację, uruchom polecenie w programie Visual Studio, tworząc projekt Pusta aplikacja (uniwersalny system Windows) i nadając mu CommonWords
nazwę . W manifeście aplikacji określ funkcję Biblioteka dokumentów, aby umożliwić aplikacji dostęp do folderu Dokumenty. Dodaj również typ pliku Text (.txt) do sekcji deklaracji manifestu aplikacji. Aby uzyskać więcej informacji na temat możliwości i deklaracji aplikacji, zobacz Tworzenie pakietów, wdrażanie i wykonywanie zapytań dotyczących aplikacji systemu Windows.
Grid
Zaktualizuj element w pliku MainPage.xaml, aby zawierał ProgressRing
element i TextBlock
element. Wskazuje ProgressRing
, że operacja jest w toku i TextBlock
pokazuje wyniki obliczeń.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ProgressRing x:Name="Progress"/>
<TextBlock x:Name="Results" FontSize="16"/>
</Grid>
Dodaj następujące #include
instrukcje do pliku pch.h.
#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>
Dodaj następujące deklaracje metody do MainPage
klasy (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);
Dodaj następujące using
instrukcje, aby MainPage.cpp.
using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
W MainPage.cpp zaimplementuj MainPage::MakeWordList
metody , MainPage::FindCommonWords
i MainPage::ShowResults
. Operacje MainPage::MakeWordList
i MainPage::FindCommonWords
wykonują operacje intensywnie korzystające z obliczeń. Metoda MainPage::ShowResults
wyświetla wynik obliczeń w interfejsie użytkownika.
// 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());
}
Zmodyfikuj konstruktor, MainPage
aby utworzyć łańcuch zadań kontynuacji wyświetlanych w interfejsie użytkownika typowymi słowami w książce Iliad by Homer. Pierwsze dwa zadania kontynuacji, które dzielą tekst na poszczególne wyrazy i znajdują wspólne słowa, mogą być czasochłonne i dlatego są jawnie ustawione do uruchamiania w tle. Końcowe zadanie kontynuacji, które aktualizuje interfejs użytkownika, nie określa kontekstu kontynuacji i dlatego jest zgodne z regułami wątkowania apartamentów.
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.
});
}
Uwaga
W tym przykładzie pokazano, jak określić konteksty wykonywania i jak utworzyć łańcuch kontynuacji. Pamiętaj, że domyślnie zadanie utworzone na podstawie operacji asynchronicznej uruchamia jego kontynuacje w mieszkaniu o nazwie task::then
. W związku z tym w tym przykładzie użyto task_continuation_context::use_arbitrary
metody , aby określić, że operacje, które nie obejmują interfejsu użytkownika, mają być wykonywane w wątku w tle.
Poniższa ilustracja przedstawia wyniki CommonWords
aplikacji.
W tym przykładzie można obsługiwać anulowanie, ponieważ task
obiekty, które obsługują create_async
używanie niejawnego tokenu anulowania. Zdefiniuj funkcję roboczą cancellation_token
, aby wykonać obiekt, jeśli zadania muszą reagować na anulowanie w sposób współpracy. Aby uzyskać więcej informacji na temat anulowania w PPL, zobacz Anulowanie w PPL