방법: WRL을 사용하여 비동기 작업 완료
이 문서에서는 Windows 런타임 C++ WRL(템플릿 라이브러리)을 사용하여 비동기 작업을 시작하고 작업이 완료되면 작업을 수행하는 방법을 보여 줍니다.
이 문서에서는 두 가지 예를 보여 줍니다. 첫 번째 예제에서는 비동기 타이머를 시작하고 타이머가 만료되기를 기다립니다. 이 예제에서는 타이머 개체를 만들 때 비동기 작업을 지정합니다. 두 번째 예제에서는 백그라운드 작업자 스레드를 실행합니다. 이 예제에서는 인터페이스를 반환하는 Windows 런타임 메서드를 IAsyncInfo
사용하는 방법을 보여줍니다. 콜백 함수는 비동기 작업의 결과를 처리하는 이벤트 처리기를 지정할 수 있기 때문에 두 예제의 중요한 부분입니다.
구성 요소의 인스턴스를 만들고 속성 값을 검색하는 보다 기본적인 예제는 방법: Windows 런타임 구성 요소 활성화 및 사용을 참조하세요.
팁
이러한 예제에서는 람다 식을 사용하여 콜백을 정의합니다. 함수 개체(functors), 함수 포인터 또는 std::function 개체를 사용할 수도 있습니다. C++ 람다 식에 대한 자세한 내용은 람다 식을 참조 하세요.
예: 타이머 작업
다음 단계에서는 비동기 타이머를 시작하고 타이머가 만료되기를 기다립니다. 전체 예제는 다음과 같습니다.
Warning
일반적으로 UWP(유니버설 Windows 플랫폼) 앱에서 Windows 런타임 C++ 템플릿 라이브러리를 사용하지만 이 예제에서는 그림에 콘솔 앱을 사용합니다. 이러한 wprintf_s
함수는 UWP 앱에서 사용할 수 없습니다. UWP 앱에서 사용할 수 있는 형식 및 함수에 대한 자세한 내용은 유니버설 Windows 플랫폼 앱에서 지원되지 않는 CRT 함수와 UWP 앱용 Win32 및 COM을 참조하세요.
#include
필요한 Windows 런타임, Windows 런타임 C++ 템플릿 라이브러리 또는 C++ 표준 라이브러리 헤더를 포함합니다.#include <Windows.Foundation.h> #include <Windows.System.Threading.h> #include <wrl/event.h> #include <stdio.h> #include <Objbase.h> using namespace ABI::Windows::Foundation; using namespace ABI::Windows::System::Threading; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers;
Windows.System.Threading.h
는 비동기 타이머를 사용하는 데 필요한 형식을 선언합니다..cpp 파일의
using namespace
지시문을 활용하여 코드를 보다 읽기 쉽게 만드는 것이 좋습니다.Windows 런타임 초기화합니다.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
인터페이스에 대한 활성화 팩터리를 만듭니다
ABI::Windows::System::Threading::IThreadPoolTimer
.// Get the activation factory for the IThreadPoolTimer interface. ComPtr<IThreadPoolTimerStatics> timerFactory; HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
Windows 런타임 정규화된 이름을 사용하여 형식을 식별합니다.
RuntimeClass_Windows_System_Threading_ThreadPoolTimer
매개 변수는 Windows 런타임 제공되고 필요한 런타임 클래스 이름을 포함하는 문자열입니다.타이머 콜백을 주 앱과 동기화하는 Event 개체를 만듭니다.
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete. // This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete. Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS)); hr = timerCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError()); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
참고 항목
이 이벤트는 콘솔 앱의 일부로만 데모용입니다. 이 예제에서는 이벤트를 사용하여 앱이 종료되기 전에 비동기 작업이 완료되도록 합니다. 대부분의 앱에서는 일반적으로 비동기 작업이 완료되기를 기다리지 않습니다.
IThreadPoolTimer
2초 후에 만료되는 개체를 만듭니다. 함수를Callback
사용하여 이벤트 처리기(개체)를ABI::Windows::System::Threading::ITimerElapsedHandler
만듭니다.// Create a timer that prints a message after 2 seconds. TimeSpan delay; delay.Duration = 20000000; // 2 seconds. auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT { wprintf_s(L"Timer fired.\n"); TimeSpan delay; HRESULT hr = timer->get_Delay(&delay); if (SUCCEEDED(hr)) { wprintf_s(L"Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0); } // Set the completion event and return. SetEvent(timerCompleted.Get()); return hr; }); hr = callback ? S_OK : E_OUTOFMEMORY; if (FAILED(hr)) { return PrintError(__LINE__, hr); } ComPtr<IThreadPoolTimer> timer; hr = timerFactory->CreateTimer(callback.Get(), delay, &timer); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
콘솔에 메시지를 인쇄하고 타이머 콜백이 완료되기를 기다립니다. 모든
ComPtr
및 RAII 개체는 범위를 벗어나면 자동으로 릴리스됩니다.// Print a message and wait for the timer callback to complete. wprintf_s(L"Timer started.\nWaiting for timer...\n"); // Wait for the timer to complete. WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE); // All smart pointers and RAII objects go out of scope here.
전체 예제는 다음과 같습니다.
// wrl-consume-async.cpp
// compile with: runtimeobject.lib
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
return hr;
}
int wmain()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
if (FAILED(initialize))
{
return PrintError(__LINE__, initialize);
}
// Get the activation factory for the IThreadPoolTimer interface.
ComPtr<IThreadPoolTimerStatics> timerFactory;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete.
// This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
hr = timerCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Create a timer that prints a message after 2 seconds.
TimeSpan delay;
delay.Duration = 20000000; // 2 seconds.
auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT
{
wprintf_s(L"Timer fired.\n");
TimeSpan delay;
HRESULT hr = timer->get_Delay(&delay);
if (SUCCEEDED(hr))
{
wprintf_s(L"Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0);
}
// Set the completion event and return.
SetEvent(timerCompleted.Get());
return hr;
});
hr = callback ? S_OK : E_OUTOFMEMORY;
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
ComPtr<IThreadPoolTimer> timer;
hr = timerFactory->CreateTimer(callback.Get(), delay, &timer);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Print a message and wait for the timer callback to complete.
wprintf_s(L"Timer started.\nWaiting for timer...\n");
// Wait for the timer to complete.
WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE);
// All smart pointers and RAII objects go out of scope here.
}
/*
Output:
Timer started.
Waiting for timer...
Timer fired.
Timer duration: 2.00 seconds.
*/
코드 컴파일
코드를 컴파일하려면 코드를 복사한 다음 Visual Studio 프로젝트에 붙여넣거나 이름이 지정된 wrl-consume-async.cpp
파일에 붙여넣은 다음 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행합니다.
cl.exe wrl-consume-async.cpp runtimeobject.lib
예: 백그라운드 스레드 작업
다음 단계에서는 작업자 스레드를 시작하고 해당 스레드에서 수행하는 작업을 정의합니다. 전체 예제는 다음과 같습니다.
팁
이 예제에서는 인터페이스를 사용하는 방법을 보여 줍니다 ABI::Windows::Foundation::IAsyncAction
. 다음을 구현하는 모든 인터페이스에 이 패턴을 적용할 수 있습니다.IAsyncInfo
IAsyncAction
IAsyncActionWithProgress
IAsyncOperation
IAsyncOperationWithProgress
#include
필요한 Windows 런타임, Windows 런타임 C++ 템플릿 라이브러리 또는 C++ 표준 라이브러리 헤더를 포함합니다.#include <Windows.Foundation.h> #include <Windows.System.Threading.h> #include <wrl/event.h> #include <stdio.h> #include <Objbase.h> using namespace ABI::Windows::Foundation; using namespace ABI::Windows::System::Threading; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers;
Windows.System.Threading.h는 작업자 스레드를 사용하는 데 필요한 형식을 선언합니다.
코드를 더 읽기 쉽게 만들려면 .cpp 파일의 지시문을 사용하는
using namespace
것이 좋습니다.Windows 런타임 초기화합니다.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
인터페이스에 대한 활성화 팩터리를 만듭니다
ABI::Windows::System::Threading::IThreadPoolStatics
.// Get the activation factory for the IThreadPoolStatics interface. ComPtr<IThreadPoolStatics> threadPool; HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
작업자 스레드 완료를 주 앱과 동기화하는 Event 개체를 만듭니다.
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete. // This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete. Event threadCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS)); hr = threadCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError()); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
참고 항목
이 이벤트는 콘솔 앱의 일부로만 데모용입니다. 이 예제에서는 이벤트를 사용하여 앱이 종료되기 전에 비동기 작업이 완료되도록 합니다. 대부분의 앱에서는 일반적으로 비동기 작업이 완료되기를 기다리지 않습니다.
메서드를
IThreadPoolStatics::RunAsync
호출하여 작업자 스레드를 만듭니다. 함수를Callback
사용하여 작업을 정의합니다.wprintf_s(L"Starting thread...\n"); // Create a thread that computes prime numbers. ComPtr<IAsyncAction> asyncAction; hr = threadPool->RunAsync(Callback<IWorkItemHandler>([&threadCompleted](IAsyncAction* asyncAction) -> HRESULT { // Print a message. const unsigned int start = 0; const unsigned int end = 100000; unsigned int primeCount = 0; for (int n = start; n < end; n++) { if (IsPrime(n)) { primeCount++; } } wprintf_s(L"There are %u prime numbers from %u to %u.\n", primeCount, start, end); // Set the completion event and return. SetEvent(threadCompleted.Get()); return S_OK; }).Get(), &asyncAction); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
함수
IsPrime
는 다음 전체 예제에서 정의됩니다.콘솔에 메시지를 인쇄하고 스레드가 완료되기를 기다립니다. 모든
ComPtr
및 RAII 개체는 범위를 벗어나면 자동으로 릴리스됩니다.// Print a message and wait for the thread to complete. wprintf_s(L"Waiting for thread...\n"); // Wait for the thread to complete. WaitForSingleObjectEx(threadCompleted.Get(), INFINITE, FALSE); wprintf_s(L"Finished.\n"); // All smart pointers and RAII objects go out of scope here.
전체 예제는 다음과 같습니다.
// wrl-consume-asyncOp.cpp
// compile with: runtimeobject.lib
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
return hr;
}
// Determines whether the input value is prime.
bool IsPrime(int n)
{
if (n < 2)
{
return false;
}
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
{
return false;
}
}
return true;
}
int wmain()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
if (FAILED(initialize))
{
return PrintError(__LINE__, initialize);
}
// Get the activation factory for the IThreadPoolStatics interface.
ComPtr<IThreadPoolStatics> threadPool;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete.
// This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
Event threadCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
hr = threadCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
wprintf_s(L"Starting thread...\n");
// Create a thread that computes prime numbers.
ComPtr<IAsyncAction> asyncAction;
hr = threadPool->RunAsync(Callback<IWorkItemHandler>([&threadCompleted](IAsyncAction* asyncAction) -> HRESULT
{
// Print a message.
const unsigned int start = 0;
const unsigned int end = 100000;
unsigned int primeCount = 0;
for (int n = start; n < end; n++)
{
if (IsPrime(n))
{
primeCount++;
}
}
wprintf_s(L"There are %u prime numbers from %u to %u.\n", primeCount, start, end);
// Set the completion event and return.
SetEvent(threadCompleted.Get());
return S_OK;
}).Get(), &asyncAction);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Print a message and wait for the thread to complete.
wprintf_s(L"Waiting for thread...\n");
// Wait for the thread to complete.
WaitForSingleObjectEx(threadCompleted.Get(), INFINITE, FALSE);
wprintf_s(L"Finished.\n");
// All smart pointers and RAII objects go out of scope here.
}
/*
Output:
Starting thread...
Waiting for thread...
There are 9592 prime numbers from 0 to 100000.
Finished.
*/
코드 컴파일
코드를 컴파일하려면 코드를 복사한 다음 Visual Studio 프로젝트에 붙여넣거나 이름이 지정된 wrl-consume-asyncOp.cpp
파일에 붙여넣은 다음 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행합니다.
cl.exe wrl-consume-asyncOp.cpp runtimeobject.lib