使用 C++ 為 Windows 市集應用程式建立非同步作業
本文件說明一些重要時應該牢記使用並行執行階段會在 Windows 市集 應用程式的非同步作業。
因為它會使應用程式保持回應使用者輸入,使用非同步程式設計是 Windows 市集 應用程式模型中的主要元件。您可以啟動長時間執行的工作,而不封鎖 UI 執行緒,因此,您可以稍後接收工作的結果。在背景執行,您也可以取消工作和接收進度通知。本文件提供 在 C++ 中的非同步程式設計 可使用 Visual C++ 來建立 Windows 市集 應用程式非同步模式的概觀。該文件教導如何對中使用及建立 Windows 執行階段 非同步作業鏈結。本節說明如何使用並行執行階段產生可以由另一個 Windows 執行階段 元件使用以及如何控制之非同步作業的非同步工作執行方式。請考慮將 非同步程式設計樣式和技巧。Hilo (使用 C++ 和 XAML 的 Windows 市集應用程式) 學習如何使用並行執行階段實作 Hilo,執行 Windows 市集 應用程式使用 C++ 和 XAML 的非同步作業。
注意事項 |
---|
您可以在 Windows 市集 應用程式中使用 平行模式程式庫 (PPL) (PPL) 和 非同步代理程式程式庫 。不過,您無法使用工作排程器或資源管理員。本文件說明並行執行階段提供對 Windows 市集 應用程式只有,且不提供傳統型應用程式的其他功能。 |
金鑰
使用 concurrency::create_async 建立其他元件 (使用可以撰寫語言除了 C++ 以外) 的非同步作業。
使用 concurrency::progress_reporter 進度通知向呼叫您的非同步作業的元件報告。
使用取消語彙基元可讓內部非同步取消作業。
create_async 函式的行為取決於傳遞給它的工作函式的傳回型別。在內容傳回同步工作的工作函式 ( task<T> 或 task<void>) 會呼叫 create_async。傳回 T 或 void 的工作函式在任意內容中執行。
您可以使用 concurrency::task::then 方法建立會執行工作的鏈結。在 Windows 市集 應用程式,工作的接續的預設內容視該工作) 建構。如果工作會藉由將非同步動作建立對工作建構函式,或是透過傳回非同步動作的 Lambda 運算式,則該工作的接續的預設內容為目前內容。如果工作沒有從非同步動作的建構,預設為任意內容為工作的繼續使用。您可以覆寫有 concurrency::task_continuation_context 類別的預設內容。
本文內容
建立非同步作業
範例:建立 C ++. Windows 執行階段元件
控制項的執行緒
範例:在 Windows 市集應用程式的控制項執行與 C++ 和 XAML
建立非同步作業
您可以在平行模式程式庫 (PPL) 中使用工作和繼續模型定義背景工作以及執行其他工作,在上一個工作完成。concurrency::task 類別提供這項功能。如需這個模型和 task 類別的詳細資訊,請參閱 工作平行處理原則 (並行執行階段)。
Windows 執行階段 是可以用來建立 Windows 市集 應用程式以特殊作業系統環境中執行的程式設計介面。這類應用程式使用授權的函式、資料型別及裝置且從 Windows 市集發佈。Windows 執行階段 是以應用程式 二進位介面 (ABI) (ABI) 表示。ABI 是讓 Windows 執行階段 應用程式可用 Visual C++ 等程式設計語言的基礎二進位合約。
使用 Windows 執行階段,您可以使用各種程式語言的最佳功能合併到應用程式。例如,您可以在 C ++. 元件可能會建立您的 JavaScript 的 UI 和執行密集的應用程式邏輯。能夠執行這些密集運算作業正在背景是保持 UI 的關鍵因素敏感。由於 task 類別特有的 C++,您必須使用 Windows 執行階段 介面進行非同步作業加入至可以撰寫語言除了 C++ 以外) 的其他元件 (。Windows 執行階段 提供可用來表示非同步作業的四個介面:
Windows::Foundation::IAsyncAction
表示非同步動作。Windows::Foundation::IAsyncActionWithProgress<TProgress>
表示報告進度之非同步動作。Windows::Foundation::IAsyncOperation<TResult>
表示傳回結果的非同步作業。Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
表示傳回結果,並報告進度之非同步作業。
動作的 概念表示非同步工作沒有產生值 (傳回 void) 的帳戶函式。作業的 概念表示非同步工作產生值。進度的 概念表示工作可能進度訊息向呼叫端報告。JavaScript、.NET Framework 和 Visual C++ 每個提供自己的方式建立這些介面執行個體為跨 ABI 界限的使用。對於 Visual C++,並行執行階段提供了 concurrency::create_async 函式。這個函式會表示工作完成的 Windows 執行階段 非同步動作或作業。create_async 函式會將工作函式 (通常是 Lambda 運算式),會在內部建立 task 在四個非同步 Windows 執行階段 介面之一中配置的物件和封裝。
注意事項 |
---|
請使用 create_async ,只有在您必須建立可從其他語言或另一個 Windows 執行階段 元件存取的功能。請直接使用 task 類別,當您知道作業由相同元件的 C++ 程式碼產生和使用。 |
create_async 的傳回型別取決於它的引數型別。例如,在中,如果您的工作不傳回值,並且不會報告進度, create_async 會傳回 IAsyncAction。如果您的工作不會傳回值也會報告進度, create_async 會傳回 IAsyncActionWithProgress。要報告的進度,請提供 concurrency::progress_reporter 物件當做參數傳遞給您的工作函式。可報告進度可報告的工作量執行,以及金額仍然保持 (例如,以百分比表示)。可用時,它也可讓您報告結果。
IAsyncAction、 IAsyncActionWithProgress<TProgress>、 IAsyncOperation<TResult>和 IAsyncActionOperationWithProgress<TProgress, TProgress> 介面每個提供可讓您取消非同步作業的 Cancel 方法。task 類別與取消語彙基元一起使用。當您使用取消語彙基元取消工作時,執行階段不會啟動訂閱這個語彙基元的新工作。工作已在使用中監視自己的取消語彙基元和停止時,它可以。這個機制在 PPL 中的取消文件中有詳細說明。您可以與 Windows 執行階段Cancel 方法的工作取消以兩種方式。首先,您可以定義您傳遞給 create_async 採用 concurrency::cancellation_token 物件的工作函式。當呼叫 Cancel 方法時,即會取消此取消語彙基元和對支援呼叫 create_async 的基礎 task 物件的一般取消規則應用。如果您未提供 cancellation_token 物件,基礎 task 物件會暗自定義一個。當您需要以合作方式回應您的工作取消時,請定義 cancellation_token 物件。範例:在 Windows 市集應用程式的控制項執行與 C++ 和 XAML 一節中 Windows 市集 應用程式示範如何執行作業會使用自訂 C++ Windows 執行階段 元件的 C# 和 XAML。
警告 |
---|
在接續工作鏈結,當 concurrency::is_task_cancellation_requested 傳回 true時,,請清除狀態再呼叫 concurrency::cancel_current_task 。如果您先前傳回而不是呼叫 cancel_current_task,以完成狀態的轉換作業而不是已取消狀態。 |
下表摘要說明您可以用來定義在應用程式的非同步作業的組合。
建立這個 Windows 執行階段 介面 |
傳回 create_async的型別。 |
透過這些參數型別指派給您的工作函式使用隱含取消語彙基元 |
透過這些參數型別指派給您的工作函式使用明確取消語彙基元 |
---|---|---|---|
IAsyncAction |
void 或 task<void> |
(無) |
(cancellation_token) |
IAsyncActionWithProgress<TProgress> |
void 或 task<void> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
IAsyncOperation<TResult> |
T 或 task<T> |
(無) |
(cancellation_token) |
IAsyncActionOperationWithProgress<TProgress, TProgress> |
T 或 task<T> |
(progress_reporter) |
(progress_reporter, cancellation_token) |
您可以傳回值或 task 物件將工作函式傳遞給 create_async 函式。這些差異會產生不同的行為。當您傳回值時,在 task 工作函式包裝,讓它在背景執行緒上執行。另外,基礎 task 使用隱含取消語彙基元。相反地,因此,如果您傳回 task 物件,執行同步執行。因此,因此,如果您傳回 task 物件,請確認您的工作函式的所有冗長作業也當做工作使應用程式保持回應性。另外,基礎 task 不使用隱含取消語彙基元。因此,您需要定義自己的工作函式接受 cancellation_token 物件是否為您需要支援取消,則會從 create_async中的 task 物件。
下列範例顯示各種建立可以由另一個 Windows 執行階段 元件使用的 IAsyncAction 物件。
// 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.
});
});
上面] [
範例:建立 C ++. Windows 執行階段元件和使用它從 C#
考慮使用 XAML 和 C# 中定義 UI 和 . C ++ Windows 執行階段 元件執行電腦密集的作業的應用程式。在此範例中, C++ 元件計算某個範圍中的數字是質數。為了說明在四 Windows 執行階段 非同步工作介面之間的差異,請啟動,在 Visual Studio 中,您可以建立 [空白方案。] 並將其命名為 填滿。然後加入方案 [Windows 執行階段元件] 專案並將其命名為 PrimesLibrary。將下列程式碼加入至產生的 C++ 標頭檔 (這個範例將 Class1.h 中重新命名為 Primes.h)。每個 public 方法定義了四個非同步介面之一。傳回值會傳回物件 Windows::Foundation::Collections::IVector<int> 的方法。報告進度產生 double 值定義整體工作的完成百分比的方法。
#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);
};
}
注意事項 |
---|
依照慣例, Windows 執行階段 中的非同步方法名稱以「Async」通常會結束。 |
將下列程式碼加入至產生的 C++ 原始程式檔 (這個範例將 Class1.cpp 重新命名為 Primes.cpp)。is_prime 函式會判斷該項目是否為質數。其餘的方法實作 Primes 類別。與方法相容它呼叫 create_async 的每個呼叫使用簽章。例如,,因為 Primes::ComputePrimesAsync 會傳回 IAsyncAction,提供給 create_async 的工作函式未傳回值,而不使用 progress_reporter 物件做為其參數。
// 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;
});
}
每個方法的第一個執行驗證以確保輸入參數為負數。如果輸入的值是負數,則方法 Platform::InvalidArgumentException會擲回。錯誤處理本節稍後加以說明。
若要從 Windows 市集 應用程式呼叫這些方法,請使用 Visual C# [空白的應用程式 (XAML)] 範本將第二個專案加入至 Visual Studio 方案。這個範例將專案命名為 填滿。然後,將 填滿 專案中,加入 PrimesLibrary 專案的參考。
將下列程式碼加入至 MainPage.xaml。這個程式碼會定義 UI,以便呼叫 C++ 元件並顯示結果。
<Page
x:Class="Primes.MainPage"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Primes"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="125"/>
<RowDefinition Height="125"/>
<RowDefinition Height="125"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<Button Name="b1" Click="computePrimes">Compute Primes</Button>
<TextBlock Name="tb1"></TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0">
<Button Name="b2" Click="computePrimesWithProgress">Compute Primes with Progress</Button>
<ProgressBar Name="pb1" HorizontalAlignment="Left" Width="100"></ProgressBar>
<TextBlock Name="tb2"></TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1">
<Button Name="b3" Click="getPrimes">Get Primes</Button>
<TextBlock Name="tb3"></TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1">
<Button Name="b4" Click="getPrimesWithProgress">Get Primes with Progress</Button>
<ProgressBar Name="pb4" HorizontalAlignment="Left" Width="100"></ProgressBar>
<TextBlock Name="tb4"></TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="2">
<Button Name="b5" Click="getPrimesHandleErrors">Get Primes and Handle Errors</Button>
<ProgressBar Name="pb5" HorizontalAlignment="Left" Width="100"></ProgressBar>
<TextBlock Name="tb5"></TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="2">
<Button Name="b6" Click="getPrimesCancellation">Get Primes with Cancellation</Button>
<Button Name="cancelButton" Click="cancelGetPrimes" IsEnabled="false">Cancel</Button>
<ProgressBar Name="pb6" HorizontalAlignment="Left" Width="100"></ProgressBar>
<TextBlock Name="tb6"></TextBlock>
</StackPanel>
</Grid>
</Page>
將下列程式碼加入至 XAML 的 MainPage 類別。這個程式碼會定義 Primes 物件與按鈕的事件處理常式。
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();
}
在非同步作業完成後,這些方法會使用 async 和 await 關鍵字更新 UI。如需 C# 和 Visual Basic 可用的非同步模式的詳細資訊, 在 Windows 市集應用程式的非同步模式與 C# 請參閱和 在 Windows 市集應用程式的非同步模式與 VB。
getPrimesCancellation 和 cancelGetPrimes 方法可讓使用者取消作業。當使用者選擇 [移除] 按鈕,取消作業的 cancelGetPrimes 方法呼叫 IAsyncOperationWithProgress<TResult, TProgress>::Cancel 。並行執行階段,會處理基礎非同步作業,擲回由 Windows 執行階段 攔截通訊的內部例外狀況型別移除已完成。如需取消模型的詳細資訊,請參閱 PPL 中的取消。
重要事項 |
---|
若要讓並行執行階段正確向 Windows 執行階段 它已取消作業,請不要攔截這個內部例外狀況型別。這表示您不應該攔截所有例外狀況 (catch (...))。如果您必須攔截所有例外狀況,以確保重新擲回的例外狀況。 Windows 執行階段 可完成取消作業。 |
在每個選項中選取之後,下圖顯示 填滿 應用程式。
如需使用 create_async 建立非同步工作可以由其他語言使用的範例,請參閱 使用這些物件的 C++ 對應旅程最佳化工具範例 和 在 C++ 中的 Windows 8 非同步作業與 PPL。
上面] [
控制項的執行緒
Windows 執行階段 使用 COM 執行緒模型。在這個模型中,物件裝載在不同的 Apartment,根據如何處理它們的同步處理。安全執行緒物件在這個多執行緒 Apartment (MTA) 裝載。必須由單一執行緒存取的物件將會在單一執行緒 Apartment (STA) 裝載。
在有 UI 的應用程式, ASTA (應用程式) STA 執行緒對提取 Windows 訊息負責和是可以更新 STA 裝載的 UI 控制項的處理序的執行緒。這有兩種結果。首先,使應用程式保持回應性,在 ASTA 執行緒不應參與任何更強大的中央處理和 I/O 動作。其次,來自背景執行緒的結果必須封送處理回 ASTA 更新 UI。在 C++. C++ Windows 市集 應用程式, MainPage 和其他 XAML 會在 ATSA 執行。因此根據預設,在 ASTA 宣告執行其中的接續工作,因此您可以更新控制項直接在繼續主體。不過,因此,如果您是以巢狀方式置於另一個工作的工作,該巢狀工作的接續在 MTA 執行。因此,要在哪些內容需要考慮明確指定這些繼續執行。
從非同步建立作業,例如 IAsyncOperation<TResult>,使用特殊語法可協助您忽略執行緒的工作詳細資料。雖然作業可能會在背景執行緒 (或此不可以由執行緒支援),其接續預設在換句話說,開始繼續作業的 Apartment 保證執行 (呼叫 task::then) 的 Apartment。您可以使用 concurrency::task_continuation_context 類別來控制繼續執行內容。使用這些靜態協助程式方法建立 task_continuation_context 物件:
使用 concurrency::task_continuation_context::use_arbitrary 指定繼續在背景執行緒上執行。
使用 concurrency::task_continuation_context::use_current 指定接續在 task::then呼叫的執行緒上執行。
您可以對 task::then 方法的 task_continuation_context 物件明確地繼續執行內容或您可以到另一個 Apartment 接著會呼叫 task::then 方法隱含控制執行內容。
重要事項 |
---|
因為 Windows 市集 應用程式主要 UI 執行緒在 STA 下,在該建立 STA 根據預設的工作會在 STA 執行。因此,在 MTA 建立接續在 MTA 執行。 |
下列各節將 UI 顯示讀取檔案從磁碟上的應用程式,會在該檔案中最常見的文字,然後顯示結果。最後的作業,更新 UI,在 UI 執行緒上。
重要事項 |
---|
這個行為是針對 Windows 市集 應用程式。如果是傳統型應用程式中,您無法控制繼續執行的位置。相反地,排程器選擇執行每個接續工作的背景工作執行緒。 |
重要事項 |
---|
不要對 STA 執行繼續主體的 concurrency::task::wait 。否則,,因為這個方法會封鎖目前的執行緒,而且可能造成應用程式變得沒有回應,執行階段會擲回 concurrency::invalid_operation 。不過,您可以呼叫 concurrency::task::get 方法收到前項工作的結果以工作的接續的。 |
上面] [
範例:在 Windows 市集 應用程式中的控制項執行與 C++ 和 XAML
考慮將檔案從磁碟的 C ++. XAML 應用程式,會在該檔案中最常見的文字,然後將結果顯示在 UI。若要建立這個應用程式,開始,在 Visual Studio 中,您可以建立 Windows 市集 [保持空白應用程式 (XAML)] 專案並將其命名為 CommonWords。在您的應用程式資訊清單,請指定 [文件庫] 功能可讓應用程式存取文件資料夾。另外再加入文字 (.txt) 檔案類型加入至應用程式資訊清單的宣告部分。如需應用程式功能和宣告的詳細資訊,請 應用程式封裝和部署參閱。
更新在 MainPage.xaml 中 Grid 項目包含 ProgressRing 項目、 TextBlock 項目。ProgressRing 表示作業正在進行和 TextBlock 顯示計算的結果。
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ProgressRing x:Name="Progress"/>
<TextBlock x:Name="Results" FontSize="16"/>
</Grid>
將下列 #include 陳述式加入至 pch.h。
#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>
將下列方法宣告加入至 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);
將下列 using 陳述式加入至 MainPage.cpp。
using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
在 MainPage.cpp,執行 MainPage::MakeWordList、 MainPage::FindCommonWords和 MainPage::ShowResults 方法。MainPage::MakeWordList 和 MainPage::FindCommonWords 執行密集運算作業。MainPage::ShowResults 方法會顯示計算的結果在 UI 中。
// 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());
}
修改 MainPage 建構函式會在 UI 中顯示共用在這本書 Word 伊利子功能 由荷 Mallory 繼續工作的鏈結。前兩個接續工作,因此將文字分隔成個別單字和尋找常見的文字,在背景很耗時和明確設定執行。最後的接續工作,更新 UI,不指定接續內容,並遵循 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.
});
}
注意事項 |
---|
這個範例示範如何指定執行內容和如何由繼續撰寫鏈結。前面說過預設會從非同步作業建立的工作執行其在呼叫 task::then的 Apartment 的接續。因此,這個範例會使用 task_continuation_context::use_arbitrary 指定不含 UI 的作業在背景執行緒執行。 |
下圖顯示 CommonWords 應用程式的結果。
在此範例中,,因為 task 物件支援 create_async 會使用隱含取消語彙基元,支援取消是可能的。定義您的工作函式接受 cancellation_token 物件您的工作是否需要回應取消以合作方式。如需取消的更多資訊 PPL 中,請參閱 PPL 中的取消
上面] [