windows C++-并发和异步操作(上)

本文介绍协同例程和 co_await 的概念,我们建议你在 UI 应用程序和非 UI 应用程序中使用它们。 为了简单起见,本介绍主题中的大多数代码示例演示了 Windows 控制台应用程序 (C++/WinRT) 项目。 本文中后面的代码示例使用协同例程,但为方便起见,控制台应用程序示例还会在退出前继续使用阻止性的 get 函数调用,这样应用程序就不会在显示其输出之前退出。 不要通过 UI 线程这样做(调用阻止性的 get 函数), 而应使用 co_await 语句。 高级并发和异步主题介绍了将要在 UI 应用程序中使用的技术。

下面介绍了可通过 C++/WinRT 创建和使用 Windows 运行时异步对象的部分方式。注意,部分代码可能需要高版本的C++标准才不会报错。

异步操作和 Windows 运行时“Async”函数

有可能需要超过 50 毫秒才能完成的任何 Windows 运行时 API 将实现为异步函数(具有一个以“Async”结尾的名称)。 异步函数的实现会启动另一线程上的工作,并且会立即返回表示异步操作的对象。 在异步操作完成后,返回的对象会包含从该工作中生成的任何值。 Windows::Foundation Windows 运行时命名空间包含四种类型的异步操作对象。

  • IAsyncAction;
  • IAsyncActionWithProgress<TProgress>;
  • IAsyncOperation<TResult>;
  • IAsyncOperationWithProgress<TResult, TProgress>;

每种异步操作类型都将投影到 winrt::Windows::Foundation C++/WinRT 命名空间中的相应类型。 C++/WinRT 还包含内部 await 适配器结构。 不要直接使用它,但借助该结构,可以编写 co_await 语句以协作等待返回其中一种异步操作类型的任何函数的结果。 然后,可以自行创作返回这些类型的协同例程。

异步 Windows 函数的示例是 SyndicationClient::RetrieveFeedAsync,其返回类型 IAsyncOperationWithProgress<TResult, TProgress> 的异步操作对象。

让我们来看一些阻止和不阻止使用 C++/WinRT 来调用类似 API 的方法。 我们将在接下来的几个代码示例中使用 Windows 控制台应用程序 (C++/WinRT) 项目,只为说明基本的概念。 更适用于 UI 应用程序的技术在高级并发和异步中讨论。

阻塞调用线程

以下代码示例接收来自 RetrieveFeedAsync 的异步操作对象,并且在该对象上调用 get 以阻塞调用线程,直到异步操作的结果可用。

若要将此示例直接复制并粘贴到 Windows 控制台应用程序 (C++/WinRT) 项目的主源代码文件中,请先在项目属性中设置“不使用预编译的头文件”。

// main.cpp
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Syndication.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

void ProcessFeed()
{
    Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    SyndicationClient syndicationClient;
    SyndicationFeed syndicationFeed{ syndicationClient.RetrieveFeedAsync(rssFeedUri).get() };
    // use syndicationFeed.
}

int main()
{
    winrt::init_apartment();
    ProcessFeed();
}

调用 get 可以方便编写代码,对于出于任何原因不想使用协同例程的控制台应用或后台线程来说,这是一种理想选择。 但这既不是并发也不是异步操作,因此不适合 UI 线程(如果试图在 UI 线程上使用它,会在未优化的版本中触发断言)。 为了避免占用 OS 线程执行其他有用的工作,我们需要另一种方法。

编写协同例程

C++/WinRT 将 C++ 协同例程集成到编程模型中以提供协作等待结果的自然方式。 可以通过编写协同例程来生成自己的 Windows 运行时异步操作。 在以下代码示例中,ProcessFeedAsync 是协同例程。

get 函数位于 C++/WinRT 投影类型 winrt::Windows::Foundation::IAsyncAction 中,因此你可以从任意 C++/WinRT 项目内部调用该函数。 你将找不到列为 IAsyncAction 接口成员的函数,因为 get 不属于实际 Windows 运行时类型 IAsyncAction 的应用程序二进制接口 (ABI) 设计面。

// main.cpp
#include <iostream>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

void PrintFeed(SyndicationFeed const& syndicationFeed)
{
    for (SyndicationItem const& syndicationItem : syndicationFeed.Items())
    {
        std::wcout << syndicationItem.Title().Text().c_str() << std::endl;
    }
}

IAsyncAction ProcessFeedAsync()
{
    Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    SyndicationClient syndicationClient;
    SyndicationFeed syndicationFeed{ co_await syndicationClient.RetrieveFeedAsync(rssFeedUri) };
    PrintFeed(syndicationFeed);
}

int main()
{
    winrt::init_apartment();

    auto processOp{ ProcessFeedAsync() };
    // do other work while the feed is being printed.
    processOp.get(); // no more work to do; call get() so that we see the printout before the application exits.
}

 协同例程是可以暂停和恢复的函数。 在上述 ProcessFeedAsync 协同例程中,当达到 co_await 语句时,该协同例程会异步启动 RetrieveFeedAsync 调用,然后立即暂停自身并将控件返回到调用方(上述示例中为 main)。 然后,main 可以继续执行工作,同时将检索并打印提要。 完成该操作(RetrieveFeedAsync 调用完成)后,ProcessFeedAsync 协同例程将在下一个语句中恢复。

可以将一个协同例程聚合到其他协同例程中。 或者,也可以调用 get 以阻塞和等待其完成(以及获得结果,如果有)。 或者,可以将其传递到支持 Windows 运行时的其他编程语言。

也可以通过使用委托来处理异步操作的已完成和/或正在进行中的事件。 

正如你所看到的,在上面的代码示例中,我们在退出 main 之前继续使用阻止性的 get 函数调用。 但是,这只是为了让应用程序不会在显示其输出之前退出。

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值