Een voorbeeld van een voltooiingsbron

Note

Als alternatief voor de voorbeeldcode in dit onderwerp is er de broncode voor een versie die gereed is voor productie van een implementatie van een taakvoltooiingsbron in de cpp-asynchrone GitHub opslagplaats.

In dit onderwerp ziet u hoe u uw eigen voltooiingsbronklasse kunt ontwerpen en gebruiken, vergelijkbaar met de TaskCompletionSource van .NET.

Broncode voor het completion_source-voorbeeld

De code in de onderstaande vermelding wordt aangeboden als voorbeeld. Het doel is om te illustreren hoe u uw eigen versie ervan kunt schrijven. Bijvoorbeeld vallen ondersteuning voor annulering en foutpropagatie buiten het bereik van dit voorbeeld.

#include <winrt/base.h>
#include <windows.h>

template <typename T>
struct completion_source
{
    completion_source()
    {
        m_signal.attach(::CreateEvent(nullptr, true, false, nullptr));
    }

    void set(T const& value)
    {
        m_value = value;
        ::SetEvent(m_signal.get());
    }

    bool await_ready() const noexcept
    {
        return ::WaitForSingleObject(m_signal.get(), 0) == 0;
    }

    void await_suspend(std::experimental::coroutine_handle<> resume)
    {
        m_wait.attach(winrt::check_pointer(::CreateThreadpoolWait(callback, resume.address(), nullptr)));
        ::SetThreadpoolWait(m_wait.get(), m_signal.get(), nullptr);
    }

    T await_resume() const noexcept
    {
        return m_value;
    }

private:

    static void __stdcall callback(PTP_CALLBACK_INSTANCE, void* context, PTP_WAIT, TP_WAIT_RESULT) noexcept
    {
        std::experimental::coroutine_handle<>::from_address(context)();
    }

    struct wait_traits
    {
        using type = PTP_WAIT;

        static void close(type value) noexcept
        {
            ::CloseThreadpoolWait(value);
        }

        static constexpr type invalid() noexcept
        {
            return nullptr;
        }
    };

    winrt::handle m_signal;
    winrt::handle_type<wait_traits> m_wait;
    T m_value{};
};

Draag de voltooiing over aan een afzonderlijke coroutine

In deze sectie ziet u één use case voor completion_source. Maak een nieuw project in Visual Studio op basis van de projectsjabloon Windows Console Application (C++/WinRT) en plak de volgende codevermelding in main.cpp (vouw de definitie van completion_source uit op basis van de vermelding in de vorige sectie).

// main.cpp
#include "pch.h"

#include <winrt/base.h>
#include <windows.h>

template <typename T>
struct completion_source
{
    ... // Paste the listing of completion_source here.
}

using namespace std::literals;
using namespace winrt;
using namespace Windows::Foundation;

fire_and_forget CompleteAfterFiveSecondsAsync(completion_source<bool>& completionSource)
{
    co_await 5s;
    completionSource.set(true);
}

IAsyncAction CompletionSourceExample1Async()
{
    completion_source<bool> completionSource;
    CompleteAfterFiveSecondsAsync(completionSource);
    co_await completionSource;
}

int main()
{
    auto asyncAction { CompletionSourceExample1Async() };
    puts("waiting");
    asyncAction.get();
    puts("done");
}

Een completion_source in een klasse inkapselen en een waarde retourneren

In dit volgende voorbeeld wordt een eenvoudige App-klasse gebruikt om een completion_source in te kapselen en een waarde te retourneren wanneer deze is voltooid. Maak een nieuw project in Visual Studio op basis van de projectsjabloon Windows Console Application (C++/WinRT) en plak de volgende codevermelding in main.cpp (vouw de definitie van completion_source uit op basis van de vermelding in de vorige sectie).

// main.cpp
#include "pch.h"

#include <winrt/base.h>
#include <windows.h>

template <typename T>
struct completion_source
{
    ... // Paste the listing of completion_source here.
}

using namespace std::literals;
using namespace winrt;
using namespace Windows::Foundation;

struct App
{
    completion_source<winrt::hstring> m_completionSource;

    IAsyncOperation<winrt::hstring> CompletionSourceExample2Async()
    {
        co_return co_await m_completionSource;
    }

    winrt::fire_and_forget CompleteAfterFiveSecondsAsync()
    {
        co_await 5s;
        m_completionSource.set(L"Hello, World!");
    }
};

int main()
{
    App app;
    auto asyncAction{ app.CompletionSourceExample2Async() };
    app.CompleteAfterFiveSecondsAsync();
    puts("waiting");
    auto message = asyncAction.get();
    printf("%ls\n", message.c_str());
}