Compartir a través de


Cómo: Completar operaciones asincrónicas mediante WRL

En este documento se muestra cómo utilizar Biblioteca de plantillas de Windows Runtime C++ (WRL) para iniciar operaciones asincrónicas y para realizar el trabajo cuando las operaciones completan.

Este documento se muestran dos ejemplos.El primer ejemplo inicia un temporizador asincrónico y espera el temporizador expire.En este ejemplo, se especifica la acción asincrónico cuando se crea el objeto de temporizador.El segundo ejemplo ejecuta un subproceso de trabajo en segundo plano.En este ejemplo se muestra cómo ejecutar un método de Windows en tiempo de ejecución que devuelve una interfaz de IAsyncInfo.La función de Devolución de llamada es una parte importante de ambos ejemplos porque les permite especificar un controlador de eventos para procesar los resultados de las operaciones asincrónicas.

Para obtener un ejemplo básico que crea una instancia de ese componente y recuperar un valor de propiedad, vea Cómo: Activar y usar un componente de Windows en tiempo de ejecución mediante WRL.

SugerenciaSugerencia

Estos ejemplos usan expresiones lambda para definir las devoluciones de llamada.También puede utilizar los objetos de función (functors), punteros a función, o los objetos de std::function.Para obtener más información sobre las expresiones lambda de C++, vea Expresiones lambda en C++.

Ejemplo: Trabajar con un temporizador

Los pasos siguientes inician un temporizador asincrónico y esperan el temporizador expire.A continuación se muestra el ejemplo completo.

Nota de precauciónPrecaución

Aunque use normalmente WRL en una aplicación de Tienda Windows, este ejemplo utiliza una aplicación de consola a la ilustración.Las funciones como wprintf_s no están disponibles de una aplicación de Tienda Windows.Para obtener más información sobre los tipos y funciones que puede utilizar en una aplicación de Tienda Windows, vea Funciones CRT no compatibles con /ZW y Win32 y COM para las aplicaciones del almacén de Windows.

  1. Incluya (#include) cualquier Windows en tiempo de ejecución necesario, WRL, o encabezado de la biblioteca estándar de 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 los tipos necesarios para usar un temporizador asincrónico.

    Recomendamos utilizar la directiva de using namespace en el archivo .cpp para hacer el código más legible.

  2. Inicializa Windows en tiempo de ejecución.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Cree un generador de activación para la interfaz 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);
    }
    

    Windows en tiempo de ejecución utiliza nombres completos para identificar tipos.El parámetro de RuntimeClass_Windows_System_Threading_ThreadPoolTimer es una cadena proporcionada por Windows en tiempo de ejecución y contiene el nombre de clase necesario en tiempo de ejecución.

  4. Cree un objeto de Evento que sincronice la devolución de temporizador a la aplicación 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);
    }
    

    [!NOTA]

    Este evento es para la demostración solo como parte de una aplicación de consola.Este ejemplo utiliza el evento para asegurarse de que una operación async completa antes de salir de la aplicación.En la mayoría de las aplicaciones, no suele esperar operaciones async para completar.

  5. Cree un objeto de IThreadPoolTimer que expire después de dos segundos.Utilice la función de Callback para crear el controlador de eventos (un 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. Imprimir un mensaje en la consola y esperan la devolución del temporizador para completar.Todo el ComPtr y objetos RAII permiten ámbito y se libera automáticamente.

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

A continuación se muestra el ejemplo 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.
*/

Hh973451.collapse_all(es-es,VS.110).gifCompilar el código

Para compilar el código, cópielo y péguelo en un proyecto de Visual Studio, o péguelo en un archivo denominado wrl-consume-async.cpp y después se ejecute el siguiente comando en una ventana de símbolo del sistema de Visual Studio.

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

Ejemplo: Trabajar con un subproceso de fondo

Los pasos siguientes se inicia un subproceso de trabajo y definen la acción que realiza ese subproceso.A continuación se muestra el ejemplo completo.

SugerenciaSugerencia

En este ejemplo se muestra cómo ejecutar la interfaz de ABI::Windows::Foundation::IAsyncAction.Puede aplicar este modelo a las interfaces que implementa IAsyncInfo: IAsyncAction, IAsyncActionWithProgress, IAsyncOperation, y IAsyncOperationWithProgress.

  1. Incluya (#include) cualquier Windows en tiempo de ejecución necesario, WRL, o encabezado de la biblioteca estándar de 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 los tipos necesarios para utilizar un subproceso de trabajo.

    Recomendamos utilizar la directiva de using namespace en el archivo .cpp para hacer el código más legible.

  2. Inicializa Windows en tiempo de ejecución.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Cree un generador de activación para la interfaz 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. Cree un objeto de Evento que sincronice el subproceso de trabajo a la aplicación 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);
        }
    
    

    [!NOTA]

    Este evento es para la demostración solo como parte de una aplicación de consola.Este ejemplo utiliza el evento para asegurarse de que una operación async completa antes de salir de la aplicación.En la mayoría de las aplicaciones, no suele esperar operaciones async para completar.

  5. Llame al método de IThreadPoolStatics::RunAsync para crear un subproceso de trabajo.Utilice la función de Callback para definir la acción.

    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);
    }
    

    La función de IsPrime se define en el ejemplo completo que sigue.

  6. Imprime un mensaje en la consola y espere el subproceso para completar.Todo el ComPtr y objetos RAII permiten ámbito y se libera automáticamente.

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

A continuación se muestra el ejemplo 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.
*/

Hh973451.collapse_all(es-es,VS.110).gifCompilar el código

Para compilar el código, cópielo y péguelo en un proyecto de Visual Studio, o péguelo en un archivo denominado wrl-consume-asyncOp.cpp y después se ejecute el siguiente comando en una ventana de símbolo del sistema de Visual Studio.

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

Vea también

Conceptos

Biblioteca de plantillas de Windows Runtime C++ (WRL)