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.

  1. 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ší.

  2. Inicializuje prostředí Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. 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.

  4. 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í.

  5. 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::ITimerElapsedHandlerobjekt).

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

  1. 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ší.

  2. Inicializuje prostředí Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. 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);
    }
    
  4. 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í.

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

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

Viz také

Knihovna šablon C++ prostředí Windows Runtime (WRL)