Como executar operações assíncronas com WRL
Este documento mostra como usar a Biblioteca de Modelos C++ do Windows Runtime (WRL) para iniciar operações assíncronas e executar o trabalho quando as operações forem concluídas.
Este documento mostra dois exemplos. O primeiro exemplo inicia um temporizador assíncrono e aguarda o temporizador expirar. Neste exemplo, você especifica a ação assíncrona ao criar o objeto temporizador. O segundo exemplo executa um thread de trabalho em segundo plano. Este exemplo mostra como trabalhar com um método do Windows Runtime que retorna uma interface IAsyncInfo
. A função Callback é uma parte importante de ambos os exemplos porque permite que eles especifiquem um manipulador de eventos para processar os resultados das operações assíncronas.
Para obter um exemplo mais básico que cria uma instância de um componente e recupera um valor da propriedade, confira Como ativar e usar um componente do Windows Runtime.
Dica
Esses exemplos usam expressões lambda para definir os retornos de chamada. Você também pode usar objetos de função (functores), ponteiros de função ou objetos std::function. Para saber mais sobre expressões lambda do C++, confira Expressões lambda.
Exemplo: Trabalhar com um temporizador
As etapas a seguir iniciam um temporizador assíncrono e esperam o temporizador expirar. O exemplo completo é mostrado a seguir.
Aviso
Embora você normalmente use a Biblioteca de Modelos C++ do Windows Runtime em um aplicativo da Plataforma Universal do Windows (UWP), este exemplo usa um aplicativo de console para ilustração. Funções como wprintf_s
não estão disponíveis em um aplicativo UWP. Para obter mais informações sobre os tipos e funções que você pode usar em um aplicativo UWP, confira Funções de CRT sem suporte em aplicativos da Plataforma Universal do Windows e Win32 e COM para aplicativos UWP.
Inclua (
#include
) qualquer Windows Runtime, Biblioteca de Modelos C++ do Windows Runtime ou cabeçalhos da Biblioteca Padrão C++ exigido.#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
declara os tipos necessários para usar um temporizador assíncrono.Recomendamos que você utilize a diretiva
using namespace
em seu arquivo .cpp para tornar o código mais legível.Inicialize o Windows Runtime.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
Crie um alocador de ativação para a interface
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); }
O Windows Runtime usa nomes totalmente qualificados para identificar tipos. O parâmetro
RuntimeClass_Windows_System_Threading_ThreadPoolTimer
é uma cadeia de caracteres fornecida pelo Windows Runtime e contém o nome da classe de runtime necessário.Crie um objeto Event que sincronize o retorno de chamada do temporizador para o aplicativo principal.
// 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); }
Observação
Esse evento é apenas para demonstração como parte de um aplicativo de console. Este exemplo usa o evento para garantir que uma operação assíncrona seja concluída antes do encerramento do aplicativo. Na maioria dos aplicativos, você normalmente não espera que as operações assíncronas sejam concluídas.
Crie um objeto
IThreadPoolTimer
que expira após dois segundos. Use a funçãoCallback
para criar o manipulador de eventos (um objetoABI::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); }
Imprima uma mensagem no console e aguarde a conclusão do retorno de chamada do temporizador. Todos os
ComPtr
e objetos RAII deixam o escopo e são liberados automaticamente.// 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.
Veja o exemplo completo:
// 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.
*/
Compilando o código
Para compilar o código, copie-o e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado wrl-consume-async.cpp
e execute o seguinte comando em uma janela do Prompt de comando do Visual Studio.
cl.exe wrl-consume-async.cpp runtimeobject.lib
Exemplo: trabalhar com um thread em segundo plano
As etapas a seguir iniciam um thread de trabalho e definem a ação executada por esse thread. O exemplo completo é mostrado a seguir.
Dica
Este exemplo demonstra como trabalhar com a interface ABI::Windows::Foundation::IAsyncAction
. Você pode aplicar esse padrão a qualquer interface que implemente IAsyncInfo
: IAsyncAction
, IAsyncActionWithProgress
, IAsyncOperation
, e IAsyncOperationWithProgress
.
Inclua (
#include
) qualquer Windows Runtime, Biblioteca de Modelos C++ do Windows Runtime ou cabeçalhos da Biblioteca Padrão C++ exigido.#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;
O Windows.System.Threading.h declara os tipos necessários para usar um thread de trabalho.
Recomendamos que você utilize a diretiva
using namespace
em seu arquivo .cpp para tornar o código mais legível.Inicialize o Windows Runtime.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
Crie um alocador de ativação para a interface
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); }
Crie um objeto Event que sincronize a conclusão do thread de trabalho com o aplicativo principal.
// 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); }
Observação
Esse evento é apenas para demonstração como parte de um aplicativo de console. Este exemplo usa o evento para garantir que uma operação assíncrona seja concluída antes do encerramento do aplicativo. Na maioria dos aplicativos, você normalmente não espera que as operações assíncronas sejam concluídas.
Chame o método
IThreadPoolStatics::RunAsync
para criar um thread de trabalho. Use a funçãoCallback
para definir a ação.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); }
A função
IsPrime
é definida no exemplo completo a seguir.Imprima uma mensagem no console e aguarde a conclusão do thread. Todos os
ComPtr
e objetos RAII deixam o escopo e são liberados automaticamente.// 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.
Veja o exemplo completo:
// 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.
*/
Compilando o código
Para compilar o código, copie-o e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado wrl-consume-asyncOp.cpp
e execute o seguinte comando em uma janela do Prompt de comando do Visual Studio.
cl.exe wrl-consume-asyncOp.cpp runtimeobject.lib
Confira também
WRL (Biblioteca de Modelos C++ do Tempo de Execução do Windows)