UWP 앱 용 c + +로 비동기 작업 만들기Creating Asynchronous Operations in C++ for UWP Apps

이 문서에서는 작업 클래스를 사용 하 여 UWP (Universal Windows 런타임) 앱에서 Windows ThreadPool 기반 비동기 작업을 생성 하는 경우 염두에 두어야 하는 몇 가지 주요 사항에 대해 설명 합니다.This document describes some of the key points to keep in mind when you use the task class to produce Windows ThreadPool-based asynchronous operations in a Universal Windows Runtime (UWP) app.

비동기 프로그래밍을 사용 하는 것은 앱이 사용자 입력에 응답을 유지할 수 있도록 하는 Windows 런타임 앱 모델의 핵심 구성 요소입니다.The use of asynchronous programming is a key component in the Windows Runtime app model because it enables apps to remain responsive to user input. UI 스레드를 차단하지 않고 장기 실행 작업을 시작할 수 있으며 해당 작업의 결과를 나중에 받을 수 있습니다.You can start a long-running task without blocking the UI thread, and you can receive the results of the task later. 또한 작업을 취소하고 작업이 백그라운드에서 실행될 때 진행률 알림을 받을 수도 있습니다.You can also cancel tasks and receive progress notifications as tasks run in the background. C + +의 비동기 프로그래밍 문서는 UWP 앱을 만드는 Visual C++에서 사용할 수 있는 비동기 패턴에 대 한 개요를 제공 합니다.The document Asynchronous programming in C++ provides an overview of the asynchronous pattern that's available in Visual C++ to create UWP apps. 이 문서에서는 비동기 Windows 런타임 작업의 체인을 사용 하 고 만드는 방법을 설명 합니다.That document teaches how to both consume and create chains of asynchronous Windows Runtime operations. 이 섹션에서는 ppltasks.h의 형식을 사용 하 여 다른 Windows 런타임 구성 요소에서 사용할 수 있는 비동기 작업을 생성 하는 방법과 비동기 작업이 실행 되는 방법을 제어 하는 방법을 설명 합니다.This section describes how to use the types in ppltasks.h to produce asynchronous operations that can be consumed by another Windows Runtime component and how to control how asynchronous work is executed. 또한 Hilo의 비동기 프로그래밍 패턴 및 팁 (c + + 및 xaml을 사용 하는 Windows 스토어 앱) 을 읽고 작업 클래스를 사용 하 여 c + + 및 xaml을 사용 하는 Windows 런타임 앱 인 Hilo에서 비동기 작업을 구현 하는 방법을 알아보세요.Also consider reading Async programming patterns and tips in Hilo (Windows Store apps using C++ and XAML) to learn how we used the task class to implement asynchronous operations in Hilo, a Windows Runtime app using C++ and XAML.

참고

UWP 앱에서 PPL ( 병렬 패턴 라이브러리 ) 및 비동기 에이전트 라이브러리 를 사용할 수 있습니다.You can use the Parallel Patterns Library (PPL) and Asynchronous Agents Library in a UWP app. 그러나 작업 스케줄러 또는 리소스 관리자는 사용할 수 없습니다.However, you cannot use the Task Scheduler or the Resource Manager. 이 문서에서는 데스크톱 앱이 아닌 UWP 앱에만 사용할 수 있는 PPL이 제공 하는 추가 기능에 대해 설명 합니다.This document describes additional features that the PPL provides that are available only to a UWP app, and not to a desktop app.

핵심 내용Key points

  • concurrency::create_async 를 사용하여 다른 구성 요소(C++ 이외의 언어로 작성될 수도 있음)에서 사용할 수 있는 비동기 작업을 만듭니다.Use concurrency::create_async to create asynchronous operations that can be used by other components (which might be written in languages other than C++).

  • concurrency::progress_reporter 를 사용하여 비동기 작업을 호출하는 구성 요소에 진행률 알림을 보고합니다.Use concurrency::progress_reporter to report progress notifications to components that call your asynchronous operations.

  • 취소 토큰을 사용하여 내부 비동기 작업이 취소될 수 있도록 합니다.Use cancellation tokens to enable internal asynchronous operations to cancel.

  • create_async 함수의 동작은 이 함수에 전달된 작업 함수의 반환 형식에 따라 달라집니다.The behavior of the create_async function depends on the return type of the work function that is passed to it. 작업( task<T> 또는 task<void>)을 반환하는 작업 함수는 create_async를 호출한 컨텍스트에서 동기적으로 실행됩니다.A work function that returns a task (either task<T> or task<void>) runs synchronously in the context that called create_async. T임의의 컨텍스트에서 반환 하거나 실행 하는 작업 함수입니다 void .A work function that returns T or void runs in an arbitrary context.

  • concurrency::task::then 메서드를 사용하여 차례로 실행되는 작업의 체인을 만들 수 있습니다.You can use the concurrency::task::then method to create a chain of tasks that run one after another. UWP 앱에서 작업의 연속에 대 한 기본 컨텍스트는 해당 작업이 생성 된 방식에 따라 달라 집니다.In a UWP app, the default context for a task's continuations depends on how that task was constructed. 비동기 동작을 작업 생성자에 전달하거나 비동기 동작을 반환하는 람다 식을 전달하여 작업을 만든 경우 해당 작업의 모든 연속에 대한 기본 컨텍스트는 현재 컨텍스트입니다.If the task was created by passing an asynchronous action to the task constructor, or by passing a lambda expression that returns an asynchronous action, then the default context for all continuations of that task is the current context. 태스크가 비동기 작업에서 생성 되지 않은 경우에는 기본적으로 작업의 연속에 대해 임의 컨텍스트가 사용 됩니다.If the task is not constructed from an asynchronous action, then an arbitrary context is used by default for the task's continuations. 기본 컨텍스트는 concurrency::task_continuation_context 클래스를 사용하여 재정의할 수 있습니다.You can override the default context with the concurrency::task_continuation_context class.

이 문서의 내용In this document

비동기 작업 만들기Creating Asynchronous Operations

PPL(병렬 패턴 라이브러리)에서 작업 및 연속 모델을 사용하여 백그라운드 작업과 이전 작업이 완료될 때 실행되는 추가 작업을 정의할 수 있습니다.You can use the task and continuation model in the Parallel Patterns Library (PPL) to define background tasks as well as additional tasks that run when the previous task completes. 이 기능은 concurrency:: task 클래스를 통해 제공됩니다.This functionality is provided by the concurrency::task class. 이 모델과 task 클래스에 대한 자세한 내용은 Task Parallelism를 호출한 컨텍스트에서 동기적으로 실행됩니다.For more information about this model and the task class, see Task Parallelism.

Windows 런타임은 특수 한 운영 체제 환경 에서만 실행 되는 UWP 앱을 만드는 데 사용할 수 있는 프로그래밍 인터페이스입니다.The Windows Runtime is a programming interface that you can use to create UWP apps that run only in a special operating system environment. 이러한 앱은 권한 있는 함수, 데이터 형식 및 장치를 사용 하며 Microsoft Store에서 배포 됩니다.Such apps use authorized functions, data types, and devices, and are distributed from the Microsoft Store. Windows 런타임은 ABI ( 응용 프로그램 이진 인터페이스 )로 표현 됩니다.The Windows Runtime is represented by the Application Binary Interface (ABI). ABI는 Visual C++와 같은 프로그래밍 언어에서 Windows 런타임 Api를 사용할 수 있도록 하는 기본 이진 계약입니다.The ABI is an underlying binary contract that makes Windows Runtime APIs available to programming languages such as Visual C++.

Windows 런타임를 사용 하 여 다양 한 프로그래밍 언어의 가장 유용한 기능을 사용 하 고 하나의 앱에 결합할 수 있습니다.By using the Windows Runtime, you can use the best features of various programming languages and combine them into one app. 예를 들어 JavaScript에서 UI를 만들고 C++ 구성 요소에서 계산이 많은 앱 논리를 수행할 수 있습니다.For example, you might create your UI in JavaScript and perform the computationally-intensive app logic in a C++ component. 이러한 계산이 많은 작업을 백그라운드에서 수행하는 기능은 UI 응답 성능을 유지하는 데 핵심적인 요소입니다.The ability to perform these computationally-intensive operations in the background is a key factor in keeping your UI responsive. task이 클래스는 c + +와 관련이 있으므로 Windows 런타임 인터페이스를 사용 하 여 다른 구성 요소 (c + + 이외의 언어로 작성 될 수도 있음)에 비동기 작업을 전달 해야 합니다.Because the task class is specific to C++, you must use a Windows Runtime interface to communicate asynchronous operations to other components (which might be written in languages other than C++). Windows 런타임는 비동기 작업을 나타내는 데 사용할 수 있는 네 가지 인터페이스를 제공 합니다.The Windows Runtime provides four interfaces that you can use to represent asynchronous operations:

Windows::Foundation::IAsyncActionWindows::Foundation::IAsyncAction
비동기 동작을 나타냅니다.Represents an asynchronous action.

Windows:: Foundation:: IAsyncActionWithProgress<TProgress>Windows::Foundation::IAsyncActionWithProgress<TProgress>
진행률을 보고하는 비동기 동작을 나타냅니다.Represents an asynchronous action that reports progress.

Windows:: Foundation:: Iasyncoperation<TResult>Windows::Foundation::IAsyncOperation<TResult>
결과를 반환하는 비동기 작업을 나타냅니다.Represents an asynchronous operation that returns a result.

Windows:: Foundation:: IAsyncOperationWithProgress<TResult, TProgress>Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
결과를 반환하고 진행률을 보고하는 비동기 작업을 나타냅니다.Represents an asynchronous operation that returns a result and reports progress.

동작 의 개념은 비동기 작업에서 값을 생성 하지 않음을 의미 합니다 (를 반환 하는 함수 라고 생각 void ).The notion of an action means that the asynchronous task doesn't produce a value (think of a function that returns void). 작업(operation) 은 비동기 작업에서 값을 생성함을 의미합니다.The notion of an operation means that the asynchronous task does produce a value. 진행률(progress) 은 작업에서 진행률 메시지를 호출자에게 보고할 수 있음을 의미합니다.The notion of progress means that the task can report progress messages to the caller. JavaScript, .NET Framework 및 Visual C++에서는 ABI 경계를 넘어 사용할 수 있도록 이러한 인터페이스의 인스턴스를 만드는 방법을 자체적으로 제공합니다.JavaScript, the .NET Framework, and Visual C++ each provides its own way to create instances of these interfaces for use across the ABI boundary. Visual C++의 경우 PPL에서 concurrency:: create_async 함수를 제공합니다.For Visual C++, the PPL provides the concurrency::create_async function. 이 함수는 작업의 완료를 나타내는 비동기 동작 또는 작업 Windows 런타임를 만듭니다.This function creates a Windows Runtime asynchronous action or operation that represents the completion of a task. create_async함수는 작업 함수 (일반적으로 람다 식)를 사용 하 여 개체를 내부적으로 만들고 task 4 개의 비동기 Windows 런타임 인터페이스 중 하나에 해당 작업을 래핑합니다.The create_async function takes a work function (typically a lambda expression), internally creates a task object, and wraps that task in one of the four asynchronous Windows Runtime interfaces.

참고

create_async다른 언어 또는 다른 Windows 런타임 구성 요소에서 액세스할 수 있는 기능을 만들어야 하는 경우에만를 사용 합니다.Use create_async only when you have to create functionality that can be accessed from another language or another Windows Runtime component. 작업이 동일한 구성 요소에서 C++ 코드를 통해 생성되고 사용되는 것을 알고 있는 경우에는 task 클래스를 직접 사용합니다.Use the task class directly when you know that the operation is both produced and consumed by C++ code in the same component.

create_async 의 반환 형식은 해당 인수의 형식에 따라 결정됩니다.The return type of create_async is determined by the type of its arguments. 예를 들어 작업 함수에서 값을 반환하지 않고 진행률을 보고하지 않는 경우 create_asyncIAsyncAction을 반환합니다.For example, if your work function doesn't return a value and doesn't report progress, create_async returns IAsyncAction. 작업 함수에서 값을 반환하지 않고 진행률을 보고하는 경우 create_asyncIAsyncActionWithProgress를 반환합니다.If your work function doesn't return a value and also reports progress, create_async returns IAsyncActionWithProgress. 진행률을 보고하려면 concurrency::progress_reporter 개체를 작업 함수에 매개 변수로 제공합니다.To report progress, provide a concurrency::progress_reporter object as the parameter to your work function. 진행률을 보고하는 기능을 사용하면 수행된 작업량과 남아 있는 작업량을 백분율 등으로 보고할 수 있습니다.The ability to report progress enables you to report what amount of work was performed and what amount still remains (for example, as a percentage). 또한 결과가 사용 가능해지면 결과도 보고할 수 있습니다.It also enables you to report results as they become available.

IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>IAsyncActionOperationWithProgress<TProgress, TProgress> 인터페이스는 비동기 작업을 취소하는 데 사용할 수 있는 Cancel 메서드를 각기 제공합니다.The IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, and IAsyncActionOperationWithProgress<TProgress, TProgress> interfaces each provide a Cancel method that enables you to cancel the asynchronous operation. task 클래스는 취소 토큰과 함께 작동합니다.The task class works with cancellation tokens. 취소 토큰을 사용하여 작업을 취소하면 런타임에서 해당 토큰을 구독하는 새 작업을 시작하지 않습니다.When you use a cancellation token to cancel work, the runtime does not start new work that subscribes to that token. 이미 활성화된 작업은 취소 토큰을 모니터링하고 가능한 경우 중지할 수 있습니다.Work that is already active can monitor its cancellation token and stop when it can. 이 메커니즘에 대해서는 Cancellation in the PPL문서에 자세히 설명되어 있습니다.This mechanism is described in greater detail in the document Cancellation in the PPL. Windows 런타임 메서드와 작업 취소를 두 가지 방법으로 연결할 수 있습니다 Cancel .You can connect task cancellation with the Windows RuntimeCancel methods in two ways. 첫째, create_async concurrency::cancellation_token 개체를 사용하도록 에 전달하는 작업 함수를 정의할 수 있습니다.First, you can define the work function that you pass to create_async to take a concurrency::cancellation_token object. Cancel메서드가 호출 되 면이 취소 토큰이 취소 되 고 일반적인 취소 규칙이 호출을 지 원하는 기본 개체에 적용 됩니다 task create_async .When the Cancel method is called, this cancellation token is canceled and the normal cancellation rules apply to the underlying task object that supports the create_async call. cancellation_token 개체를 제공하지 않는 경우 내부 task 개체는 암시적으로 이 개체를 정의합니다.If you do not provide a cancellation_token object, the underlying task object defines one implicitly. 작업 함수에서 취소에 협조적으로 응답해야 하는 경우에는 cancellation_token 개체를 정의해야 합니다.Define a cancellation_token object when you need to cooperatively respond to cancellation in your work function. 예제: c + + 및 xaml을 사용 하 여 Windows 런타임 앱에서 실행 제어 에서는 사용자 지정 Windows 런타임 c + + 구성 요소를 사용 하는 c # 및 xaml을 사용 하 여 UWP (유니버설 Windows 플랫폼) 앱에서 취소를 수행 하는 방법의 예를 보여 줍니다The section Example: Controlling Execution in a Windows Runtime App with C++ and XAML shows an example of how to perform cancellation in a Universal Windows Platform (UWP) app with C# and XAML that uses a custom Windows Runtime C++ component.

경고

작업 연속 체인에서 항상 상태를 정리한 다음 취소 토큰이 취소 되 면 concurrency:: cancel_current_task 를 호출 합니다.In a chain of task continuations, always clean up state and then call concurrency::cancel_current_task when the cancellation token is canceled. cancel_current_task를 호출하지 않고 조기에 반환하는 경우 작업이 취소된 상태가 아니라 완료된 상태로 전환됩니다.If you return early instead of calling cancel_current_task, the operation transitions to the completed state instead of the canceled state.

다음 표에는 앱에서 비동기 작업을 정의하는 데 사용할 수 있는 조합이 요약되어 있습니다.The following table summarizes the combinations that you can use to define asynchronous operations in your app.

이 Windows 런타임 인터페이스를 만들려면To create this Windows Runtime interface create_asyncReturn this type from create_async 암시적 취소 토큰을 사용하기 위해 작업 함수에 전달할 매개 변수 형식Pass these parameter types to your work function to use an implicit cancellation token 명시적 취소 토큰을 사용하기 위해 작업 함수에 전달할 매개 변수 형식Pass these parameter types to your work function to use an explicit cancellation token
IAsyncAction void 디스크나task<void>void or task<void> (없음)(none) (cancellation_token)(cancellation_token)
IAsyncActionWithProgress<TProgress> void 디스크나task<void>void or task<void> (progress_reporter)(progress_reporter) (progress_reporter, cancellation_token)(progress_reporter, cancellation_token)
IAsyncOperation<TResult> T 또는 task<T>T or task<T> (없음)(none) (cancellation_token)(cancellation_token)
IAsyncActionOperationWithProgress<TProgress, TProgress> T 또는 task<T>T or task<T> (progress_reporter)(progress_reporter) (progress_reporter, cancellation_token)(progress_reporter, cancellation_token)

task 함수에 전달하는 작업 함수에서 create_async 개체를 반환하거나 값을 반환할 수 있습니다.You can return a value or a task object from the work function that you pass to the create_async function. 이러한 변형은 각기 다른 동작을 생성합니다.These variations produce different behaviors. 값을 반환하는 경우 작업 함수는 백그라운드 스레드에서 실행될 수 있도록 task 에 래핑됩니다.When you return a value, the work function is wrapped in a task so that it can be run on a background thread. 또한 내부 task 는 암시적 취소 토큰을 사용합니다.In addition, the underlying task uses an implicit cancellation token. 반대로 task 개체를 반환하는 경우 작업 함수는 동기적으로 실행됩니다.Conversely, if you return a task object, the work function runs synchronously. 따라서 task 개체를 반환하는 경우에는 앱이 응답 성능을 유지할 수 있도록 작업 함수에서 시간이 많이 걸리는 모든 작업이 작업으로 실행되어야 합니다.Therefore, if you return a task object, ensure that any lengthy operations in your work function also run as tasks to enable your app to remain responsive. 또한 내부 task 는 암시적 취소 토큰을 사용하지 않습니다.In addition, the underlying task does not use an implicit cancellation token. 따라서 cancellation_token 에서 task 개체를 반환할 때 취소에 대한 지원이 필요한 경우 create_async개체를 사용하도록 작업 함수를 정의해야 합니다.Therefore, you need to define your work function to take a cancellation_token object if you require support for cancellation when you return a task object from create_async.

다음 예제에서는 IAsyncAction 다른 Windows 런타임 구성 요소에서 사용할 수 있는 개체를 만드는 다양 한 방법을 보여 줍니다.The following example shows the various ways to create an IAsyncAction object that can be consumed by another Windows Runtime component.

// 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에서 사용#Example: Creating a C++ Windows Runtime Component and Consuming it from C#

XAML 및 c #을 사용 하 여 계산 집약적인 작업을 수행 하는 UI 및 c + + Windows 런타임 구성 요소를 정의 하는 앱을 고려 합니다.Consider an app that uses XAML and C# to define the UI and a C++ Windows Runtime component to perform compute-intensive operations. 이 예에서 C++ 구성 요소는 지정된 범위에서 소수인 수를 계산합니다.In this example, the C++ component computes which numbers in a given range are prime. 네 Windows 런타임 가지 비동기 작업 인터페이스 간의 차이점을 보여 주기 위해 Visual Studio에서 빈 솔루션 을 만들고 이름을로 지정 하 여 시작 Primes 합니다.To illustrate the differences among the four Windows Runtime asynchronous task interfaces, start, in Visual Studio, by creating a Blank Solution and naming it Primes. 그런 다음 솔루션에 Windows 런타임 구성 요소 프로젝트를 추가하고 이름을 PrimesLibrary로 지정합니다.Then add to the solution a Windows Runtime Component project and naming it PrimesLibrary. 생성된 C++ 헤더 파일에 다음 코드를 추가합니다(이 예에서는 Class1.h의 이름을 Primes.h로 변경).Add the following code to the generated C++ header file (this example renames Class1.h to Primes.h). public 메서드는 네 가지 비동기 인터페이스 중 하나를 정의 합니다.Each public method defines one of the four asynchronous interfaces. 값을 반환 하는 메서드는 Windows:: Foundation:: Collections:: IVector <int> 개체를 반환 합니다.The methods that return a value return a Windows::Foundation::Collections::IVector<int> object. 진행률을 보고 하는 메서드는 double 완료 된 전체 작업의 백분율을 정의 하는 값을 생성 합니다.The methods that report progress produce double values that define the percentage of overall work that has completed.

#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"로 끝납니다.By convention, asynchronous method names in the Windows Runtime typically end with "Async".

생성된 C++ 소스 파일에 다음 코드를 추가합니다(이 예에서는 Class1.cpp의 이름을 Primes.cpp로 변경).Add the following code to the generated C++ source file (this example renames Class1.cpp to Primes.cpp). is_prime 함수는 입력이 소수인지 여부를 확인합니다.The is_prime function determines whether its input is prime. 나머지 메서드는 Primes 클래스를 구현합니다.The remaining methods implement the Primes class. create_async 를 호출할 때마다 이 함수가 호출된 메서드와 호환되는 서명이 사용됩니다.Each call to create_async uses a signature that's compatible with the method from which it is called. 예를 들어 Primes::ComputePrimesAsyncIAsyncAction을 반환하기 때문에 create_async 에 제공되는 작업 함수는 값을 반환하지 않으며 progress_reporter 개체를 매개 변수로 사용하지 않습니다.For example, because Primes::ComputePrimesAsync returns IAsyncAction, the work function that's provided to create_async doesn't return a value and doesn't take a progress_reporter object as its 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;
    });
}

각 메서드는 먼저 유효성 검사를 수행 하 여 입력 매개 변수가 음수가 아닌지 확인 합니다.Each method first performs validation to ensure that the input parameters are non-negative. 입력 값이 음수이면 메서드에서 Platform::InvalidArgumentException을 throw합니다.If an input value is negative, the method throws Platform::InvalidArgumentException. 오류 처리에 대해서는 이 섹션의 뒷부분에서 설명합니다.Error handling is explained later in this section.

UWP 앱에서 이러한 메서드를 사용 하려면 Visual c # Blank app (XAML) 템플릿을 사용 하 여 두 번째 프로젝트를 visual Studio 솔루션에 추가 합니다.To consume these methods from a UWP app, use the Visual C# Blank App (XAML) template to add a second project to the Visual Studio solution. 이 예에서는 프로젝트 이름을 Primes로 지정합니다.This example names the project Primes. 그런 다음 Primes 프로젝트에서 PrimesLibrary 프로젝트에 대한 참조를 추가합니다.Then, from the Primes project, add a reference to the PrimesLibrary project.

MainPage.xaml에 다음 코드를 추가합니다.Add the following code to MainPage.xaml. 이 코드에서는 C++ 구성 요소를 호출하고 결과를 표시할 수 있도록 UI를 정의합니다.This code defines the UI so that you can call the C++ component and display results.

<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>

MainPage.xaml의 MainPage 클래스에 다음 코드를 추가합니다.Add the following code to the MainPage class in MainPage.xaml. 이 코드에서는 Primes 개체와 단추 이벤트 처리기를 정의합니다.This code defines a Primes object and the button event handlers.

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();
}

이러한 메서드는 asyncawait 키워드를 사용하여 비동기 작업이 완료된 후 UI를 업데이트합니다.These methods use the async and await keywords to update the UI after the asynchronous operations complete. UWP 앱의 비동기 코딩에 대 한 자세한 내용은 스레딩 및 비동기 프로그래밍을 참조 하세요.For information about asynchronous coding in UWP apps, see Threading and async programming.

getPrimesCancellationcancelGetPrimes 메서드는 함께 작동하여 사용자가 작업을 취소할 수 있도록 합니다.The getPrimesCancellation and cancelGetPrimes methods work together to enable the user to cancel the operation. 사용자가 취소 단추를 선택 하면 cancelGetPrimes 메서드가 IAsyncOperationWithProgress <TResult, TProgress> :: cancel 을 호출 하 여 작업을 취소 합니다.When the user chooses the Cancel button, the cancelGetPrimes method calls IAsyncOperationWithProgress<TResult, TProgress>::Cancel to cancel the operation. 기본 비동기 작업을 관리 하는 동시성 런타임는 취소가 완료 되었음을 알리기 위해 Windows 런타임에서 catch 한 내부 예외 형식을 throw 합니다.The Concurrency Runtime, which manages the underlying asynchronous operation, throws an internal exception type that's caught by the Windows Runtime to communicate that cancellation has completed. 취소 모델에 대 한 자세한 내용은 취소를 참조 하세요.For more information about the cancellation model, see Cancellation.

중요

PPL에서 작업을 취소 한 Windows 런타임에 올바르게 보고할 수 있도록 하려면이 내부 예외 형식을 catch 하지 마십시오.To enable the PPL to correctly report to the Windows Runtime that it has canceled the operation, do not catch this internal exception type. 즉, 모든 예외도 catch하지 않아야 합니다(catch (...)).This means that you should also not catch all exceptions (catch (...)). 모든 예외를 catch 해야 하는 경우 예외를 다시 throw 하 여 Windows 런타임 취소 작업을 완료할 수 있는지 확인 합니다.If you must catch all exceptions, rethrow the exception to ensure that the Windows Runtime can complete the cancellation operation.

다음 그림에서는 각 옵션을 선택한 후의 Primes 앱을 보여 줍니다.The following illustration shows the Primes app after each option has been chosen.

Windows 런타임 Prime 앱Windows Runtime Primes app

를 사용 하 여 create_async 다른 언어에서 사용할 수 있는 비동기 작업을 만드는 예제는 Bing 지도 여행 최적화 프로그램 샘플에서 c + + 사용을 참조 하세요.For an example that uses create_async to create asynchronous tasks that can be consumed by other languages, see Using C++ in the Bing Maps Trip Optimizer sample.

실행 스레드 제어Controlling the Execution Thread

Windows 런타임는 COM 스레딩 모델을 사용 합니다.The Windows Runtime uses the COM threading model. 이 모델에서 개체는 동기화를 처리하는 방식에 따라 서로 다른 아파트에 호스트됩니다.In this model, objects are hosted in different apartments, depending on how they handle their synchronization. 스레드로부터 안전한 개체는 MTA(다중 스레드 아파트)에서 호스트됩니다.Thread-safe objects are hosted in the multi-threaded apartment (MTA). 단일 스레드에서 액세스해야 하는 개체는 STA(단일 스레드 아파트)에서 호스트됩니다.Objects that must be accessed by a single thread are hosted in a single-threaded apartment (STA).

UI가 있는 앱에서 ASTA(애플리케이션 STA) 스레드는 창 메시지를 펌프하는 작업을 담당하며 STA에서 호스트된 UI 컨트롤을 업데이트할 수 있는 프로세스의 유일한 스레드입니다.In an app that has a UI, the ASTA (Application STA) thread is responsible for pumping window messages and is the only thread in the process that can update the STA-hosted UI controls. 이에 따라 두 가지 결과가 발생합니다.This has two consequences. 첫째, 앱의 응답 성능을 유지하기 위해 CPU를 많이 사용하는 모든 I/O 작업은 ASTA 스레드에서 실행되지 않아야 합니다.First, to enable the app to remain responsive, all CPU-intensive and I/O operations should not be run on the ASTA thread. 둘째, 백그라운드 스레드에서 제공되는 결과는 UI를 업데이트하기 위해 ASTA에 다시 마샬링되어야 합니다.Second, results that come from background threads must be marshaled back to the ASTA to update the UI. C + + UWP 앱에서 MainPage 및 다른 XAML 페이지는 모두 ATSA에서 실행 됩니다.In a C++ UWP app, MainPage and other XAML pages all run on the ATSA. 따라서 ASTA에서 선언된 작업 연속은 기본적으로 그곳에서 실행되므로 연속 본문에서 직접 컨트롤을 업데이트할 수 있습니다.Therefore, task continuations that are declared on the ASTA are run there by default so you can update controls directly in the continuation body. 그러나 작업을 다른 작업에 중첩하는 경우 중첩된 작업의 모든 연속은 MTA에서 실행됩니다.However, if you nest a task in another task, any continuations on that nested task run in the MTA. 따라서 이러한 연속이 실행되는 컨텍스트를 명시적으로 지정할지 여부를 고려해야 합니다.Therefore, you need to consider whether to explicitly specify on what context these continuations run.

IAsyncOperation<TResult>과 같은 비동기 작업에서 만든 작업은 스레딩 세부 사항을 무시하는 데 도움이 될 수 있는 특수한 의미 체계를 사용합니다.A task that's created from an asynchronous operation, such as IAsyncOperation<TResult>, uses special semantics that can help you ignore the threading details. 작업이 백그라운드 스레드에서 실행되거나 스레드를 통해 전혀 지원되지 않을 수 있지만 작업의 연속은 연속 작업을 시작한 아파트(즉, task::then을 호출한 아파트)에서 실행되도록 기본적으로 보장됩니다.Although an operation might run on a background thread (or it may not be backed by a thread at all), its continuations are by default guaranteed to run on the apartment that started the continuation operations (in other words, from the apartment that called task::then). concurrency::task_continuation_context 클래스를 사용하여 연속의 실행 컨텍스트를 제어할 수 있습니다.You can use the concurrency::task_continuation_context class to control the execution context of a continuation. task_continuation_context 개체를 만들려면 다음과 같은 정적 도우미 메서드를 사용합니다.Use these static helper methods to create task_continuation_context objects:

task_continuation_context 개체를 task:: then 메서드에 전달하여 연속의 실행 컨텍스트를 명시적으로 제어하거나, 작업을 다른 아파트에 전달한 다음 task::then 메서드를 호출하여 실행 컨텍스트를 암시적으로 제어할 수 있습니다.You can pass a task_continuation_context object to the task::then method to explicitly control the execution context of the continuation or you can pass the task to another apartment and then call the task::then method to implicitly control the execution context.

중요

UWP 앱의 주 UI 스레드가 STA에서 실행 되기 때문에 해당 STA에서 만드는 연속은 기본적으로 STA에서 실행 됩니다.Because the main UI thread of UWP apps run under STA, continuations that you create on that STA by default run on the STA. 따라서 MTA에서 만드는 연속은 MTA에서 실행됩니다.Accordingly, continuations that you create on the MTA run on the MTA.

다음 섹션에서는 디스크에서 파일을 읽고 해당 파일에서 가장 일반적인 단어를 찾은 다음 UI에 결과를 표시하는 앱을 보여 줍니다.The following section shows an app that reads a file from disk, finds the most common words in that file, and then shows the results in the UI. UI를 업데이트하는 최종 작업은 UI 스레드에서 발생합니다.The final operation, updating the UI, occurs on the UI thread.

중요

이 동작은 UWP 앱에만 적용 됩니다.This behavior is specific to UWP apps. 데스크톱 앱의 경우 연속이 실행되는 위치를 제어하지 않습니다.For desktop apps, you do not control where continuations run. 대신 스케줄러가 각 연속을 실행할 작업자 스레드를 선택합니다.Instead, the scheduler chooses a worker thread on which to run each continuation.

중요

STA에서 실행되는 연속의 본문에서 concurrency::task::wait 를 호출하지 마세요.Do not call concurrency::task::wait in the body of a continuation that runs on the STA. 호출하는 경우 이 메서드가 현재 스레드를 차단하고 앱이 응답하지 않게 만들 수 있기 때문에 런타임에서 concurrency::invalid_operation 을 throw합니다.Otherwise, the runtime throws concurrency::invalid_operation because this method blocks the current thread and can cause the app to become unresponsive. 그러나 concurrency::task::get 메서드를 호출하여 작업 기반 연속에서 선행 작업의 결과를 받을 수 있습니다.However, you can call the concurrency::task::get method to receive the result of the antecedent task in a task-based continuation.

예: c + + 및 XAML을 사용 하 여 Windows 런타임 앱에서 실행 제어Example: Controlling Execution in a Windows Runtime App with C++ and XAML

디스크에서 파일을 읽고 해당 파일에서 가장 일반적인 단어를 찾은 다음 UI에 결과를 표시하는 C++ XAML 앱을 살펴보겠습니다.Consider a C++ XAML app that reads a file from disk, finds the most common words in that file, and then shows the results in the UI. 이 앱을 만들려면 Visual Studio에서 비어 있는 앱 (유니버설 Windows) 프로젝트를 만들고 이름을로 지정 하 여 시작 CommonWords 합니다.To create this app, start, in Visual Studio, by creating a Blank App (Universal Windows) project and naming it CommonWords. 앱 매니페스트에서 문서 라이브러리 접근 권한을 지정하여 앱이 문서 폴더에 액세스할 수 있도록 합니다.In your app manifest, specify the Documents Library capability to enable the app to access the Documents folder. 또한 텍스트(.txt) 파일 형식을 앱 매니페스트의 선언 섹션에 추가합니다.Also add the Text (.txt) file type to the declarations section of the app manifest. 앱 기능 및 선언에 대 한 자세한 내용은 Windows 앱의 패키징, 배포 및 쿼리를 참조 하세요.For more information about app capabilities and declarations, see Packaging, deployment, and query of Windows apps.

MainPage.xaml에서 Grid 요소를 업데이트하여 ProgressRing 요소와 TextBlock 요소를 포함합니다.Update the Grid element in MainPage.xaml to include a ProgressRing element and a TextBlock element. ProgressRing 은 작업이 진행 중임을 나타내고, TextBlock 은 계산의 결과를 보여 줍니다.The ProgressRing indicates that the operation is in progress and the TextBlock shows the results of the computation.

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <ProgressRing x:Name="Progress"/>
    <TextBlock x:Name="Results" FontSize="16"/>
</Grid>

#include Pch에 다음 문을 추가 합니다.Add the following #include statements to pch.h.

#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>

MainPage 클래스에 다음 메서드 선언을 추가합니다(MainPage.h).Add the following method declarations to the MainPage class (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에 추가 합니다.Add the following using statements to MainPage.cpp.

using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;

MainPage.cpp에서 MainPage::MakeWordList, MainPage::FindCommonWordsMainPage::ShowResults 메서드를 구현합니다.In MainPage.cpp, implement the MainPage::MakeWordList, MainPage::FindCommonWords, and MainPage::ShowResults methods. MainPage::MakeWordListMainPage::FindCommonWords 는 계산이 많은 작업을 수행합니다.The MainPage::MakeWordList and MainPage::FindCommonWords perform computationally-intensive operations. MainPage::ShowResults 메서드는 UI에 계산의 결과를 표시합니다.The MainPage::ShowResults method displays the result of the computation in the 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 생성자를 수정하여 Homer의 The Iliad 라는 책에 있는 일반적인 단어를 UI에 표시하는 연속 작업의 체인을 만듭니다.Modify the MainPage constructor to create a chain of continuation tasks that displays in the UI the common words in the book The Iliad by Homer. 텍스트를 개별 단어로 분할하고 일반적인 단어를 찾는 처음 두 연속 작업은 시간이 걸릴 수 있으므로 백그라운드에서 실행되도록 명시적으로 설정됩니다.The first two continuation tasks, which split the text into individual words and find common words, can be time consuming and are therefore explicitly set to run in the background. UI를 업데이트하는 최종 연속 작업은 연속 컨텍스트를 지정하지 않으므로 아파트 스레딩 규칙을 따릅니다.The final continuation task, which updates the UI, specifies no continuation context, and therefore follows the apartment threading rules.

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.
    });
}

참고

이 예에서는 실행 컨텍스트를 지정하는 방법과 연속의 체인을 구성하는 방법을 보여 줍니다.This example demonstrates how to specify execution contexts and how to compose a chain of continuations. 앞에서 설명했듯이 기본적으로 비동기 작업에서 만든 작업은 task::then을 호출한 아파트에서 연속을 실행합니다.Recall that by default a task that's created from an asynchronous operation runs its continuations on the apartment that called task::then. 따라서 이 예에서는 task_continuation_context::use_arbitrary 를 사용하여 UI와 관련이 없는 작업이 백그라운드 스레드에서 수행되도록 지정합니다.Therefore, this example uses task_continuation_context::use_arbitrary to specify that operations that do not involve the UI be performed on a background thread.

다음 그림에서는 CommonWords 앱의 결과를 보여 줍니다.The following illustration shows the results of the CommonWords app.

Windows 런타임 CommonWords 앱Windows Runtime CommonWords app

이 예제에서는를 task 지 원하는 개체가 암시적 취소 토큰을 사용 하기 때문에 취소를 지원할 수 있습니다 create_async .In this example, it's possible to support cancellation because the task objects that support create_async use an implicit cancellation token. 작업이 협조적으로 취소에 응답해야 하는 경우 cancellation_token 개체를 사용하도록 작업 함수를 정의합니다.Define your work function to take a cancellation_token object if your tasks need to respond to cancellation in a cooperative manner. PPL에서의 취소에 대한 자세한 내용은 Cancellation in the PPL을 참조하세요.For more info about cancellation in the PPL, see Cancellation in the PPL

참고 항목See also

동시성 런타임Concurrency Runtime