Postupy: Dokončení asynchronních operací s použitím knihovny WRL
Tento dokument ukazuje, jak pomocí knihovny šablon jazyka C++ (WRL) prostředí Windows Runtime spustit asynchronní operace a provádět práci po dokončení operací.
Tento dokument popisuje dva příklady. První příklad spustí asynchronní časovač a počká na vypršení platnosti časovače. V tomto příkladu zadáte asynchronní akci při vytváření objektu časovače. Druhý příklad spustí pracovní vlákno na pozadí. Tento příklad ukazuje, jak pracovat s prostředí Windows Runtime metodouIAsyncInfo
, která vrací rozhraní. Funkce Zpětné volání je důležitou součástí obou příkladů, protože umožňuje určit obslužnou rutinu události ke zpracování výsledků asynchronních operací.
Základní příklad, který vytvoří instanci komponenty a načte hodnotu vlastnosti, naleznete v tématu Postupy: Aktivace a použití prostředí Windows Runtime Komponenta.
Tip
Tyto příklady používají výrazy lambda k definování zpětných volání. Můžete také použít objekty funkcí (functory), ukazatele funkce nebo objekty std::function . Další informace o výrazech lambda jazyka C++ naleznete v tématu Výrazy lambda.
Příklad: Práce s časovačem
Následující kroky spustí asynchronní časovač a počká na vypršení platnosti časovače. Úplný příklad následuje.
Upozorňující
I když obvykle používáte prostředí Windows Runtime knihovnu šablon C++ v aplikaci Univerzální platforma Windows (UPW), tento příklad používá konzolovou aplikaci pro ilustraci. Funkce, jako wprintf_s
jsou například, nejsou dostupné z aplikace pro UPW. Další informace o typech a funkcích, které můžete použít v aplikaci pro UPW, najdete v tématu Funkce CRT, které nejsou podporovány v aplikacích pro Univerzální platforma Windows a Win32 a COM pro aplikace pro UPW.
Zahrňte (
#include
) všechny požadované prostředí Windows Runtime, prostředí Windows Runtime knihovnu šablon jazyka C++ nebo hlavičky standardní knihovny jazyka 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
deklaruje typy potřebné k použití asynchronního časovače.Doporučujeme použít direktivu
using namespace
v souboru .cpp, aby byl kód čitelnější.Inicializuje prostředí Windows Runtime.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
Vytvořte aktivační továrnu
ABI::Windows::System::Threading::IThreadPoolTimer
pro rozhraní.// 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); }
Prostředí Windows Runtime k identifikaci typů používá plně kvalifikované názvy. Parametr
RuntimeClass_Windows_System_Threading_ThreadPoolTimer
je řetězec, který poskytuje prostředí Windows Runtime a obsahuje požadovaný název třídy modulu runtime.Vytvořte objekt události, který synchronizuje zpětné volání časovače do hlavní aplikace.
// 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); }
Poznámka:
Tato událost je určená pouze pro ukázku jako součást konzolové aplikace. Tento příklad používá událost k zajištění dokončení asynchronní operace před ukončením aplikace. Ve většině aplikací obvykle nečekáte na dokončení asynchronních operací.
IThreadPoolTimer
Vytvořte objekt, jehož platnost vyprší po dvou sekundách.Callback
Pomocí funkce vytvořte obslužnou rutinu události (ABI::Windows::System::Threading::ITimerElapsedHandler
objekt).// 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); }
Vytiskněte zprávu do konzoly a počkejte na dokončení zpětného volání časovače. Všechny
ComPtr
objekty a objekty RAII ponechají obor a uvolní se automaticky.// 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.
Tady je úplný příklad:
// 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.
*/
Probíhá kompilace kódu
Pokud chcete kód zkompilovat, zkopírujte ho a vložte ho do projektu sady Visual Studio nebo ho vložte do pojmenovaného wrl-consume-async.cpp
souboru a potom v okně příkazového řádku sady Visual Studio spusťte následující příkaz.
cl.exe wrl-consume-async.cpp runtimeobject.lib
Příklad: Práce s vláknem na pozadí
Následující kroky spustí pracovní vlákno a definuje akci prováděnou tímto vláknem. Úplný příklad následuje.
Tip
Tento příklad ukazuje, jak pracovat s rozhraním ABI::Windows::Foundation::IAsyncAction
. Tento model můžete použít na libovolné rozhraní, které implementuje IAsyncInfo
: IAsyncAction
, IAsyncActionWithProgress
, IAsyncOperation
, a IAsyncOperationWithProgress
.
Zahrňte (
#include
) všechny požadované prostředí Windows Runtime, prostředí Windows Runtime knihovnu šablon jazyka C++ nebo hlavičky standardní knihovny jazyka 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 deklaruje typy potřebné k použití pracovního vlákna.
Doporučujeme použít direktivu
using namespace
v souboru .cpp, aby byl kód čitelnější.Inicializuje prostředí Windows Runtime.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
Vytvořte aktivační továrnu
ABI::Windows::System::Threading::IThreadPoolStatics
pro rozhraní.// 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); }
Vytvořte objekt události, který synchronizuje dokončení pracovního vlákna s hlavní aplikací.
// 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); }
Poznámka:
Tato událost je určená pouze pro ukázku jako součást konzolové aplikace. Tento příklad používá událost k zajištění dokončení asynchronní operace před ukončením aplikace. Ve většině aplikací obvykle nečekáte na dokončení asynchronních operací.
IThreadPoolStatics::RunAsync
Voláním metody vytvořte pracovní vlákno.Callback
Pomocí funkce definujte akci.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); }
Funkce
IsPrime
je definována v úplném příkladu, který následuje.Vytiskněte zprávu do konzoly a počkejte na dokončení vlákna. Všechny
ComPtr
objekty a objekty RAII ponechají obor a uvolní se automaticky.// 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.
Tady je úplný příklad:
// 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.
*/
Probíhá kompilace kódu
Pokud chcete kód zkompilovat, zkopírujte ho a vložte ho do projektu sady Visual Studio nebo ho vložte do pojmenovaného wrl-consume-asyncOp.cpp
souboru a potom v okně příkazového řádku sady Visual Studio spusťte následující příkaz.
cl.exe wrl-consume-asyncOp.cpp runtimeobject.lib