Beispiel für eine Abschlussquelle

In diesem Thema wird veranschaulicht, wie Sie eine eigene Abschlussquellklasse ähnlich TaskCompletionSource von .NET erstellen und verwenden können.

Quellcode für das completion_source-Beispiel

Der Code in der folgenden Auflistung dient als Beispiel. Der Zweck besteht darin, zu veranschaulichen, wie Sie Ihre eigene Version davon schreiben können. Die Unterstützung für Abbruch und Fehlerweitergabe würde beispielsweise den Rahmen dieses Beispiels sprengen.

#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{};
};

Auslagern des Abschlusses des Vorgangs in eine separate Coroutine

In diesem Abschnitt wird ein Anwendungsfall für completion_source veranschaulicht. Erstellen Sie ein neues Projekt in Visual Studio basierend auf der Projektvorlage Windows-Konsolenanwendung (C++/WinRT) , und fügen Sie die folgende Codeauflistung in main.cpp ein (indem Sie die Definition von completion_source basierend auf der Auflistung im vorherigen Abschnitt erweitern).

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

Kapseln von completion_source in einer Klasse und Zurückgeben eines Werts

Im nächsten Beispiel wird eine einfache App-Klasse verwendet, um ein completion_source-Objekt zu kapseln und nach Abschluss des Vorgangs einen Wert zurückzugeben. Erstellen Sie ein neues Projekt in Visual Studio basierend auf der Projektvorlage Windows-Konsolenanwendung (C++/WinRT) , und fügen Sie die folgende Codeauflistung in main.cpp ein (indem Sie die Definition von completion_source basierend auf der Auflistung im vorherigen Abschnitt erweitern).

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