Bagikan melalui


Cara: Menyelesaikan Operasi Asinkron Menggunakan WRL

Dokumen ini menunjukkan cara menggunakan Windows Runtime C++ Template Library (WRL) untuk memulai operasi asinkron dan melakukan pekerjaan ketika operasi selesai.

Dokumen ini memperlihatkan dua contoh. Contoh pertama memulai timer asinkron dan menunggu timer kedaluwarsa. Dalam contoh ini, Anda menentukan tindakan asinkron saat membuat objek timer. Contoh kedua menjalankan utas pekerja latar belakang. Contoh ini menunjukkan cara bekerja dengan metode Windows Runtime yang mengembalikan IAsyncInfo antarmuka. Fungsi Callback adalah bagian penting dari kedua contoh karena memungkinkan mereka menentukan penanganan aktivitas untuk memproses hasil operasi asinkron.

Untuk contoh yang lebih mendasar yang membuat instans komponen dan mengambil nilai properti, lihat Cara: Mengaktifkan dan Menggunakan Komponen Runtime Windows.

Tip

Contoh-contoh ini menggunakan ekspresi lambda untuk menentukan panggilan balik. Anda juga dapat menggunakan objek fungsi (functors), penunjuk fungsi, atau objek std::function . Untuk informasi selengkapnya tentang ekspresi lambda C++, lihat Ekspresi Lambda.

Contoh: Bekerja dengan Timer

Langkah-langkah berikut memulai timer asinkron dan menunggu timer kedaluwarsa. Contoh lengkapnya adalah sebagai berikut.

Peringatan

Meskipun Anda biasanya menggunakan Pustaka Templat Windows Runtime C++ di aplikasi Platform Windows Universal (UWP), contoh ini menggunakan aplikasi konsol untuk ilustrasi. Fungsi seperti wprintf_s tidak tersedia dari aplikasi UWP. Untuk informasi selengkapnya tentang jenis dan fungsi yang dapat Anda gunakan di aplikasi UWP, lihat Fungsi CRT yang tidak didukung di aplikasi Platform Windows Universal dan Win32 dan COM untuk aplikasi UWP.

  1. Sertakan (#include) Runtime Windows yang diperlukan, Pustaka Templat Windows Runtime C++, atau header Pustaka Standar 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 menyatakan jenis yang diperlukan untuk menggunakan timer asinkron.

    Kami menyarankan agar Anda menggunakan arahan using namespace dalam file .cpp Anda untuk membuat kode lebih mudah dibaca.

  2. Menginisialisasi Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Buat pabrik aktivasi untuk ABI::Windows::System::Threading::IThreadPoolTimer antarmuka.

    // 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 Runtime menggunakan nama yang sepenuhnya memenuhi syarat untuk mengidentifikasi jenis. Parameter RuntimeClass_Windows_System_Threading_ThreadPoolTimer adalah string yang disediakan oleh Windows Runtime dan berisi nama kelas runtime yang diperlukan.

  4. Buat objek Peristiwa yang menyinkronkan panggilan balik timer ke aplikasi utama.

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

    Catatan

    Acara ini hanya untuk demonstrasi sebagai bagian dari aplikasi konsol. Contoh ini menggunakan peristiwa untuk memastikan bahwa operasi asinkron selesai sebelum aplikasi keluar. Di sebagian besar aplikasi, Anda biasanya tidak menunggu operasi asinkron selesai.

  5. Buat IThreadPoolTimer objek yang kedaluwarsa setelah dua detik. Callback Gunakan fungsi untuk membuat penanganan aktivitas (ABI::Windows::System::Threading::ITimerElapsedHandlerobjek).

    // 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. Cetak pesan ke konsol dan tunggu hingga panggilan balik timer selesai. Semua ComPtr objek RAII dan meninggalkan cakupan dan dirilis secara otomatis.

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

Berikut adalah contoh lengkapnya:

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

Mengompilasi Kode

Untuk mengkompilasi kode, salin lalu tempelkan dalam proyek Visual Studio, atau tempelkan dalam file yang diberi nama wrl-consume-async.cpp lalu jalankan perintah berikut di jendela Prompt Perintah Visual Studio.

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

Contoh: Bekerja dengan Utas Latar Belakang

Langkah-langkah berikut memulai utas pekerja dan menentukan tindakan yang dilakukan oleh utas tersebut. Contoh lengkapnya adalah sebagai berikut.

Tip

Contoh ini menunjukkan cara bekerja dengan ABI::Windows::Foundation::IAsyncAction antarmuka. Anda dapat menerapkan pola ini ke antarmuka apa pun yang mengimplementasikan : , , IAsyncActionWithProgressIAsyncOperation, dan IAsyncOperationWithProgress. IAsyncActionIAsyncInfo

  1. Sertakan (#include) Runtime Windows yang diperlukan, Pustaka Templat Windows Runtime C++, atau header Pustaka Standar 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 mendeklarasikan jenis yang diperlukan untuk menggunakan utas pekerja.

    Kami menyarankan agar Anda menggunakan direktif using namespace dalam file .cpp Anda untuk membuat kode lebih mudah dibaca.

  2. Menginisialisasi Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Buat pabrik aktivasi untuk ABI::Windows::System::Threading::IThreadPoolStatics antarmuka.

    // 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. Buat objek Peristiwa yang menyinkronkan penyelesaian utas pekerja ke aplikasi utama.

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

    Catatan

    Acara ini hanya untuk demonstrasi sebagai bagian dari aplikasi konsol. Contoh ini menggunakan peristiwa untuk memastikan bahwa operasi asinkron selesai sebelum aplikasi keluar. Di sebagian besar aplikasi, Anda biasanya tidak menunggu operasi asinkron selesai.

  5. IThreadPoolStatics::RunAsync Panggil metode untuk membuat utas pekerja. Callback Gunakan fungsi untuk menentukan tindakan.

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

    Fungsi IsPrime ini didefinisikan dalam contoh lengkap yang mengikuti.

  6. Cetak pesan ke konsol dan tunggu hingga utas selesai. Semua ComPtr objek RAII dan meninggalkan cakupan dan dirilis secara otomatis.

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

Berikut adalah contoh lengkapnya:

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

Mengompilasi Kode

Untuk mengkompilasi kode, salin lalu tempelkan dalam proyek Visual Studio, atau tempelkan dalam file yang diberi nama wrl-consume-asyncOp.cpp lalu jalankan perintah berikut di jendela Prompt Perintah Visual Studio.

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

Baca juga

Pustaka Templat Windows Runtime C++ (WRL)