用 C++ 为 Windows 应用商店应用程序创建异步操作

本文档描述一些要点记住要在使用并发运行时会在 Windows 应用商店 app 的异步操作。

因为它可以使 apps 保持响应对用户输入,使用异步编程是在 Windows 应用商店 app 模型中的主要组件。可以启动的任务的任务,而不必阻止 UI 线程,因此,您以后可以接收任务的结果。也可以取消任务和接收进度通知作为在后台运行的任务。本文档提供 异步编程。C++ 可用在 Visual C++ 中创建 Windows 应用商店 apps 异步模式的概述。该文档讲述如何请使用和创建异步操作 Windows 运行时 链。本节介绍如何使用并发运行时生成可由另一个 Windows 运行时 元素以及如何使用控件的异步操作异步工作方式执行。也可考虑读取 异步编程模式以及有关在 Hilo 使用 C++ 和 XAML,(windows 存储 apps) 了解我们如何在 Hilo 使用并发运行时实现异步操作,Windows 应用商店 app 使用 C++ 和 XAML。

说明说明

在 Windows 应用商店 app 可以使用 并行模式库 (ppl) 和 异步代理库。但是,不能使用任务计划程序或资源管理器。本文档介绍并发运行时提供对 Windows 应用商店 app 可用,并且不对桌面应用程序的其他功能。

关键点

  • 使用 concurrency::create_async 创建可由其他组件 (使用可能编写语言除了 C++) 的异步操作。

  • 使用 concurrency::progress_reporter 进度通知到调用异步操作的元素报告。

  • 使用取消标记来支持内部异步操作取消。

  • create_async 功能的行为取决于传递给它的工作函数的返回类型。返回任务的工作函数 ( task<T>task<void>) 在调用 create_async的上下文同步运行。返回 Tvoid 的工作函数在任意上下文中运行。

  • 可以使用 concurrency::task::then 方法创建依次运行任务的链接。在 Windows 应用商店 app,任务的延续的默认值上下文取决于该任务构造完成。如果任务传递一个异步操作创建对任务构造函数,或者通过将返回一个异步操作的 lambda 表达式,则该任务的所有延续的默认值上下文是当前上下文。如果任务不会从异步操作构造,则默认情况下任意上下文是项的继续使用。您可以重写具有 concurrency::task_continuation_context 选件类的默认值上下文。

本文档的内容

  • 创建异步操作

  • 示例:创建 c. c++ 窗口运行时组件。

  • 控件执行线程

  • 示例:控件执行在具有 C++ 和 XAML 的窗口存储 App

创建异步操作

可以在并行模式库 (PPL) 中使用任务和继续设计定义后台任务以及运行的其他任务,在前面的任务完成。concurrency::task 选件类提供此功能。有关此模型和 task 选件类的更多信息,请参见" 任务并行(并发运行时)

Windows 运行时 是一个可用于创建 Windows 应用商店 apps 在特定操作系统环境中只运行的编程接口。授权这样的 apps 使用从 Windows 应用商店函数,数据类型和设备和分配。Windows 运行时 由 应用程序二进制接口 (abi) (ABI) 表示。ABI 是一 Windows 运行时 API 可用编程语言 (如 Visual C++ 的基础二进制协定。

使用 Windows 运行时,您可以使用各种编程语言的最佳功能和合并到一个 app。例如,可以在 JavaScript 在 c. c++ 组件可能创建您的 UI 和执行计算密集型 app 的逻辑。能够执行这些计算密集型操作在后台是在将您的 UI 的一个关键因素响应。由于 task 选件类特定于 C++,必须使用 Windows 运行时 接口异步通信操作改为可能编写语言除了 C++) 的其他组件 (。Windows 运行时 提供可用于表示异步操作的四个接口:

事件的 用途意味着异步任务不生成值 (返回 void) 的思考功能。操作的 概念意味着异步任务生成值。进度的 概念意味着任务可能进度消息向调用方报告。JavaScript、.NET framework 和 Visual C++ 每个元素提供自己的方式创建这些接口实例。在 ABI 边界的使用。对于 Visual C++,并发运行时提供 concurrency::create_async 功能。此函数创建表示任务完成后 Windows 运行时 异步操作。create_async 函数采用一个函数 (通常 lambda 表达式),在四个异步 Windows 运行时 接口之一在内部创建 task 对象和换行该任务。

说明说明

请使用 create_async,只有 + 当必须创建可从其他语言或另一个 Windows 运行时 元素访问的功能。请直接使用 task 选件类,在您知道操作由同一元素的 C++ 代码生成和使用。

create_async 的返回类型取决于其参数的类型。例如,因此,如果您的工作函数不返回值,并且不报告进度,create_async 返回 IAsyncAction。如果您的工作函数不返回值和报告进度,create_async 返回 IAsyncActionWithProgress。若要报告进度,请提供一 concurrency::progress_reporter 对象作为参数传递给您的工作函数。能够报告进度可以报告哪些工作的执行了,以及金额仍保持 (例如,为百分比)。当它们不断增多,还可以报告结果。

IAsyncActionIAsyncActionWithProgress<TProgress>IAsyncOperation<TResult>IAsyncActionOperationWithProgress<TProgress, TProgress> 接口每个提供使您可以取消异步操作的一个 Cancel 方法。task 选件类使用取消标记一起使用。当使用一个取消标记来取消工作时,运行时不会开始订阅该标记的新工作。工作已经有效可监视其取消标记和停止,则可以。此机制在文档 PPL 中的取消操作更详细地介绍。可以使用 Windows 运行时Cancel 方法来任务取消通过两种方式。首先,您可以定义传递给 create_async 采用 concurrency::cancellation_token 对象的工作函数。当 Cancel 调用方法时,此取消标记被取消,并且常规取消规则应用于基础 task 支持 create_async 调用的对象。如果未提供一 cancellation_token 对象,基础 task 对象隐式定义一。在需要协作方式响应在您的工作函数时,的取消请定义一 cancellation_token 对象。该节 示例:控件执行在具有 C++ 和 XAML 的窗口存储 App 在 Windows 应用商店 app 演示了如何执行取消与使用自定义 Windows 运行时 C++ 组件的 C# 和 XAML。

警告说明警告

在任务延续链,当 concurrency::is_task_cancellation_requested 返回 true时,始终清理状态然后调用 concurrency::cancel_current_task。如果早已返回而不是调用 cancel_current_task,与已完成状态的操作转换而不是已取消状态。

下表总结了可用于定义将在您的应用程序的异步操作的组合。

创建此 Windows 运行时 接口

返回从 create_async的此类型

通过这些参数类型为您的工作函数使用隐式取消标记

通过这些参数类型为您的工作函数使用显式取消标记

IAsyncAction

voidtask<void>

(无)

(cancellation_token)

IAsyncActionWithProgress<TProgress>

voidtask<void>

(progress_reporter)

(progress_reporter, cancellation_token)

IAsyncOperation<TResult>

Ttask<T>

(无)

(cancellation_token)

IAsyncActionOperationWithProgress<TProgress, TProgress>

Ttask<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. c++ 窗口运行时组件和使用它从 C#

考虑使用 XAML 和 C# 定义 UI 和 c. c++ Windows 运行时 元素执行计算密集型操作 app 的。在此示例中,C++ 组件计算在特定范围的哪些数字是不是质数。为了演示在四 Windows 运行时 异步任务接口之间的区别,请启动,在 Visual Studio 中,通过创建 空白解决方案 并将其命名为 Primes。然后添加到解决方案一个 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 应用商店 app 的这些方法,请使用 Visual C# 空白应用程序 (XAML) 模板添加第二个项添加到 Visual Studio 解决方案。此示例将项目命名为 Primes。然后,从 Primes 项目中,添加对 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>

将以下代码添加到 MainPage.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 可用的异步模式的信息,请参见 在窗口的异步模式存储在 C# 中 apps在窗口的异步模式存储在 VB 中 apps

getPrimesCancellationcancelGetPrimes 方法允许用户取消操作。当用户选择 取消 按钮时,cancelGetPrimes 调用 IAsyncOperationWithProgress<TResult, TProgress>::Cancel 方法移除操作。并发运行时,管理基础异步操作,引发由 Windows 运行时 捕获通信的内部异常类型取消已完成。有关取消模型的更多信息,请参见 PPL 中的取消操作

重要说明重要事项

若要使并发运行时正确到 Windows 运行时 其报告取消了操作,不要捕捉此内部异常类型。这意味着您也不应捕捉所有异常 (catch (...))。如果必须捕捉所有异常,再次引发相应的异常 Windows 运行时 可以完成取消操作。

每个选项后,下图显示 Primes app。

Windows 应用商店 Primes 应用程序

有关使用 create_async 创建异步任务可供其他语言使用的示例,请参见 使用堆中的 C++ 映射行程优化程序示例窗口在 C++ 的 8 个异步操作使用 PPL

[顶级]

控件执行线程

Windows 运行时 使用 COM 线程模型。在此模型中,对象的方式承载在不同的单元,该图按照处理自己的同步。线程安全的对象处于多线程单元 (MTA) 承载。必须由单个线程访问的对象处于单线程单元 (STA) 承载。

在具有 UI 的应用程序,ASTA (应用程序) STA 线程传递发送 windows 消息负责和在无法更新 STA 承载的 UI 控件的进程的线程。这有两种结果。首先,这使得 app 保持响应,在 ASTA 线程不应参与所有强化中心处理和 I/O 操作。接下来,来自后台线程的结果必须封送回到 ASTA 更新 UI。在 c. c++ Windows 应用商店 app,MainPage 和其他 XAML 在 ATSA 调用任何运行。因此,默认情况下在 ASTA 声明运行存在的任务延续,因此您可以更新控件直接在延续主体。但是,因此,如果嵌套在另一个任务的任务,每次的所有继续嵌套在 MTA 运行的任务。因此,您是否在什么上下文需要考虑显式指定这些继续运行。

从异步操作后,例如 **IAsyncOperation<TResult>**的任务,使用可帮助您忽略线程处理特定细节的语义。尽管操作可能运行到后台线程 (或此不能由线程返回),该异步默认情况下在换言之,开始继续操作的单元确保运行 (从调用 task::then) 的单元。可以使用 concurrency::task_continuation_context 选件类控件继续执行上下文。使用这些静态帮助器方法创建 task_continuation_context 对象:

可以传递到 task::then 方法的一 task_continuation_context 对象显式控制继续执行上下文或通过任务到另一个单元然后调用 task::then 方法隐式控件执行上下文。

重要说明重要事项

由于 Windows 应用商店 apps 主 UI 线程上运行在 STA 下,您每次创建 STA 默认情况下为继续在 STA 运行。因此,您在 MTA 创建的延续在 MTA 运行。

以下各节在 UI 显示读取文件从磁盘 app 的,请在中查找该文件的最常见的单词,然后显示结果。最终操作,更新 UI,在 UI 线程上。

重要说明重要事项

此行为是特定于 Windows 应用商店 apps。对于桌面 apps,您无法控制继续运行的位置。相反,计划程序选择运行每个延续的辅助线程。

重要说明重要事项

不要对在 STA 运行继续主体的 concurrency::task::wait。否则,方法,因为此方法以阻止当前线程,并可能会导致该 app 无响应,运行时将引发 concurrency::invalid_operation。但是,可以调用 concurrency::task::get 方法接收前面的任务的结果在基于任务的延续的。

[顶级]

示例:在 Windows 应用商店 App 的控件执行与 C++ 和 XAML

考虑读取文件从磁盘的 c. c++ XAML app,会在该文件的最常见的单词,然后显示结果在 UI。若要创建此 app,启动,在 Visual Studio 中,通过创建 Windows 应用商店空白应用程序 (XAML) 项目并将其命名为 CommonWords。在清单您的应用程序,请指定 文档库 功能使该 app 到的访问文档文件夹。另外添加文本 (.txt) 文件类型到清单中的 app 的声明部分。有关 app 功能和说明的更多信息,请 App 程序和部署包参见。

更新在 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::MakeWordListMainPage::FindCommonWordsMainPage::ShowResults 方法。MainPage::MakeWordListMainPage::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 伊利亚特 由荷马延续任务的链接。前两个延续任务,因此将文本转换为各个词并找到常用单词,在后台很耗时和显式设置运行。最终延续任务,更新 UI,不指定延续上下文,并遵循单元线程处理规则。

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的单元中继续。因此,此示例使用 task_continuation_context::use_arbitrary 指定不涉及 UI 操作在后台线程上执行。

下图显示 CommonWords app 的结果。

Windows 应用商店 CommonWords 应用程序

在此示例中,支持移除,因为支持 create_async 使用隐式取消标记的 task 对象是可能的。定义您的工作函数采用 cancellation_token 对象中的任务是否需要响应取消以协作的方式。有关取消操作的更多信息 PPL,请参见 PPL 中的取消操作

[顶级]

请参见

概念

并发运行时