Partilhar via


Como executar operações assíncronas com WRL

Este documento mostra como usar Biblioteca em Tempo de Execução C++ do Tempo de Execução do Windows (WRL) para iniciar operações assíncronas e executar o trabalho quando as operações forem concluídos.

Este documento mostra dois exemplos. O primeiro exemplo inicia um timer assíncrona e aguarda o timer para expirar. Neste exemplo, você especifica a ação assíncrono quando você cria o objeto de timer. O segundo exemplo executa um thread de trabalho em segundo plano. Este exemplo mostra como trabalhar com um método de Tempo de Execução do Windows que retorna uma interface de IAsyncInfo . A função de Retorno de chamada é uma parte importante de ambos os exemplos a seguir porque os habilita para especificar 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 desse componente e recupera um valor de propriedade, consulte Como ativar e usar um componente de Tempo de Execução do Windows com WRL.

Dica

Esses exemplos usam expressões de lambda para definir os retornos de chamada.Você também pode usar objetos de função (functors), ponteiros de função, ou objetos de std::function .Para obter mais informações sobre expressões de lambda C++, consulte Expressões lambda em C++.

Exemplo: Trabalhando com um timer

As seguintes etapas iniciam um timer assíncrona e esperam o timer para expirar. O exemplo completo a seguir.

Aviso

Embora você normalmente usa WRL em um aplicativo de Windows Store , esse exemplo usa um aplicativo de console para ilustração.Funções como wprintf_s não estão disponíveis em um aplicativo de Windows Store .Para obter mais informações sobre os tipos e funções que você pode usar em um aplicativo de Windows Store , consulte Funções de CRT não suportadas por /ZW e O Win32 e COM para aplicativos do Windows Store.

  1. Inclua ()#includequalquer Tempo de Execução do Windowsnecessário, WRL, ou cabeçalhos de biblioteca padrão do 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 declara os tipos que são necessários para usar um timer assíncrona.

    Recomendamos que você utiliza a política de using namespace no arquivo .cpp para tornar código mais legível.

  2. Inicializar Tempo de Execução do Windows.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Crie uma fábrica de ativação para a interface de 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);
    }
    

    Os nomes totalmente qualificados de Tempo de Execução do Windows para identificar tipos. O parâmetro de RuntimeClass_Windows_System_Threading_ThreadPoolTimer é uma cadeia de caracteres que é fornecida por Tempo de Execução do Windows e contém o nome da classe necessário em tempo de execução.

  4. Crie um objeto de Evento que sincroniza o retorno de chamada de timer 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);
    }
    

    Dica

    Esse evento é para demonstração somente como parte de um aplicativo de console.Este exemplo usa o evento para assegurar que uma operação de async foi concluído antes que o aplicativo vai.Na maioria dos aplicativos, você normalmente não espera operações de async para concluir.

  5. Crie um objeto de IThreadPoolTimer que expire após dois segundos. Use a função de Callback para criar o manipulador de eventos (um objeto de 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);
    }
    
  6. Imprime uma mensagem para o console e aguarde o retorno de chamada de timer para concluir. Qualquer ComPtr e os objetos de RAII permitem 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.
    

Aqui está 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-a e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado wrl-consume-async.cpp e execute o comando a seguir em uma janela de prompt de comando do Visual Studio.

cl.exe wrl-consume-async.cpp runtimeobject.lib

Exemplo: Trabalhando com um thread em segundo plano

As seguintes etapas iniciará um thread de trabalho e definem a ação que é executada por esse thread. O exemplo completo a seguir.

Dica

Este exemplo demonstra como trabalhar com a interface de ABI::Windows::Foundation::IAsyncAction .Você pode aplicar esse padrão para qualquer interface que implementar IAsyncInfo: IAsyncAction, IAsyncActionWithProgress, IAsyncOperation, e IAsyncOperationWithProgress.

  1. Inclua ()#includequalquer Tempo de Execução do Windowsnecessário, WRL, ou cabeçalhos de biblioteca padrão do 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 declara os tipos que são necessários para usar um thread de trabalho.

    Recomendamos que você use a política de using namespace no arquivo .cpp para tornar código mais legível.

  2. Inicializar Tempo de Execução do Windows.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Crie uma fábrica de ativação para a interface de 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);
    }
    
  4. Crie um objeto de Evento que sincroniza a conclusão do thread de trabalho 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 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);
        }
    

    Dica

    Esse evento é para demonstração somente como parte de um aplicativo de console.Este exemplo usa o evento para assegurar que uma operação de async foi concluído antes que o aplicativo vai.Na maioria dos aplicativos, você normalmente não espera operações de async para concluir.

  5. Chame o método de IThreadPoolStatics::RunAsync para criar um thread de trabalho. Use a função de Callback 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 de IsPrime é definida no exemplo completo a seguir.

  6. Imprime uma mensagem para o console e aguarde o thread para concluir. Qualquer ComPtr e os objetos de RAII permitem 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.
    

Aqui está 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-a e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado wrl-consume-asyncOp.cpp e execute o comando a seguir em uma janela de prompt de comando do Visual Studio.

cl.exe wrl-consume-asyncOp.cpp runtimeobject.lib

Consulte também

Conceitos

Biblioteca de Modelos C++ do Tempo de Execução do Windows (WRL)