Condividi tramite


Procedura: completare operazioni asincrone mediante WRL

In questo documento viene illustrato come utilizzare Libreria di modelli di Windows Runtime C++ (WRL) per avviare le operazioni asincrone ed eseguire lavoro quando le operazioni completate.

In questo documento vengono illustrate due esempi.Il primo esempio viene avviato un timer asincrono e attende che il timer in modo che scada.In questo esempio, specificare l'operazione asincrona quando si crea l'oggetto timer.Nel secondo esempio viene eseguito su un thread del componente BackgroundWorker.In questo esempio viene illustrato come utilizzare un metodo di Windows Runtime che restituisce un'interfaccia di IAsyncInfo.La funzione di Callback rappresenta una parte importante di entrambi gli esempi in quanto consente di specificare un gestore eventi per elaborare i risultati di operazioni asincrone.

Per un esempio più semplice che crea un'istanza del componente e recupera il valore della proprietà, vedere Procedura: attivare e utilizzare un componente Windows Runtime mediante WRL.

SuggerimentoSuggerimento

In questi esempi si utilizzano espressioni lambda per definire i callback.È anche possibile utilizzare oggetti funzione (functors), puntatori a funzione, oggetti o di std::function.Per ulteriori informazioni sulle espressioni lambda C++, vedere Espressioni lambda in C++.

Esempio: Utilizza un timer

I passaggi seguenti avviano un timer asincrono e attendono il timer in modo che scada.Di seguito viene fornito l'esempio completo.

Nota di avvisoAttenzione

Sebbene in genere utilizzato WRL in un'applicazione di Windows Store, questo esempio un'applicazione console per completezza.Funzioni come wprintf_s non sono disponibili da un'applicazione di Windows Store.Per ulteriori informazioni sui tipi e le funzioni che è possibile utilizzare in un'applicazione di Windows Store, vedere Funzioni CRT non supportate con /ZW e Win32 e COM per le applicazioni di archivio di Windows.

  1. Importare ()#includequalsiasi Windows Runtimeobbligatorio, WRL, o intestazioni standard di 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 dichiara i tipi necessari per utilizzare un timer asincrono.

    Si consiglia di utilizzare la direttiva di using namespace nel file CPP per rendere il codice più leggibile.

  2. Inizializzare Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Creare una factory di attivazione per l'interfaccia di 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);
    }
    

    I nomi completi di utilizzo di Windows Runtime per identificare i tipi.Il parametro di RuntimeClass_Windows_System_Threading_ThreadPoolTimer è una stringa fornita da Windows Runtime e contiene il nome di classe di runtime richiesto.

  4. Creare un oggetto di Evento che sincronizza il callback del timer all'applicazione principale.

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

    Questo evento è solo a scopo dimostrativo come parte di un'applicazione console.In questo esempio viene utilizzato l'evento per garantire che un'operazione async prima di completare la chiusura dell'applicazione.Nella maggior parte delle applicazioni, in genere non si attende le operazioni async per completare.

  5. Creare un oggetto di IThreadPoolTimer che scade dopo due secondi.Utilizzare la funzione di Callback per creare il gestore eventi (un oggetto di 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. Stampa un messaggio sulla console e attendere il callback del timer per completare.Qualsiasi ComPtr e gli oggetti RAII consentono all'ambito e vengono eliminati 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.
    

Di seguito è riportato l'esempio 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(it-it,VS.110).gifCompilazione del codice

Per compilare il codice, copiarlo e quindi incollarlo in un progetto di Visual Studio, oppure incollarlo in un file denominato wrl-consume-async.cpp quindi viene eseguito il comando seguente in una finestra del prompt dei comandi di Visual Studio.

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

Esempio: Utilizza un thread in background

I passaggi seguenti avviano un thread di lavoro e definisce l'azione che viene eseguita da tale thread.Di seguito viene fornito l'esempio completo.

SuggerimentoSuggerimento

In questo esempio viene illustrato come utilizzare l'interfaccia di ABI::Windows::Foundation::IAsyncAction.È possibile applicare questo modello a un'interfaccia che implementa IAsyncInfo: IAsyncAction, IAsyncActionWithProgress, IAsyncOperatione IAsyncOperationWithProgress.

  1. Importare ()#includequalsiasi Windows Runtimeobbligatorio, WRL, o intestazioni standard di 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 dichiara i tipi necessari per utilizzare un thread di lavoro.

    Si consiglia di utilizzare la direttiva di using namespace nel file CPP per rendere il codice più leggibile.

  2. Inizializzare Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Creare una factory di attivazione per l'interfaccia di 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. Creare un oggetto di Evento che sincronizza il completamento del thread di lavoro all'applicazione principale.

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

    Questo evento è solo a scopo dimostrativo come parte di un'applicazione console.In questo esempio viene utilizzato l'evento per garantire che un'operazione async prima di completare la chiusura dell'applicazione.Nella maggior parte delle applicazioni, in genere non si attende le operazioni async per completare.

  5. Chiamare il metodo di IThreadPoolStatics::RunAsync per creare un thread di lavoro.Utilizzare la funzione di Callback per definire l'azione.

    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 funzione di IsPrime è definita nell'esempio completo che segue.

  6. Stampa un messaggio sulla console e attendere il thread.Qualsiasi ComPtr e gli oggetti RAII consentono all'ambito e vengono eliminati 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.
    

Di seguito è riportato l'esempio 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(it-it,VS.110).gifCompilazione del codice

Per compilare il codice, copiarlo e quindi incollarlo in un progetto di Visual Studio, oppure incollarlo in un file denominato wrl-consume-asyncOp.cpp quindi viene eseguito il comando seguente in una finestra del prompt dei comandi di Visual Studio.

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

Vedere anche

Concetti

Libreria di modelli di Windows Runtime C++ (WRL)