C++ WinRT中的异步

C++/WinRT 是微软为 Windows 运行时提供的现代 C++17 语言投影。

2018 年引入。Windows SDK 版本 10.0.17134.0(Windows 10 1803)。

本文不介绍C++ WinRT其他只介绍它提供的异步能力:

异步操作方法

假设我有一个耗时很久的方法,我不希望它阻塞UI线程,那你就可以这样做:

cpp 复制代码
#include <dispatcherqueue.h>
#include <winrt/base.h> 
#include <winrt/Windows.System.h>
#include <winrt/Windows.Foundation.h>
winrt::Windows::Foundation::IAsyncAction readImg()
{
    co_await winrt::resume_background();
    //这里省略了耗时很长的逻辑代码
    co_await winrt::resume_foreground(dq);
}

这里,winrt::Windows::Foundation::IAsyncAction 代表一个无结果,无进度的异步操作。

除此之外,WinRT提供了如下异步操作类型

类型 返回值 进度报告 使用场景
IAsyncAction 简单异步操作
IAsyncActionWithProgress<T> 需要进度反馈的操作
IAsyncOperation<T> 返回结果的异步操作
IAsyncOperationWithProgress<T, P> 返回结果且需进度的操作

如果你要在一个方法中使用 co_await 操作异步逻辑,那么你就要把这个方法标记为 co_await 兼容方法,比如返回 IAsyncAction 类型。

co_await winrt::resume_background() 是 C++/WinRT 提供的线程切换辅助函数,用于从当前线程切换到后台线程(背后有池线程支持)。这个操作是非阻塞的。

co_await winrt::resume_foreground(dq) 负责从后台线程切换到当前线程。

是不是比C++标准库里的异步操作要简单的多呢?

dq是什么?

线程任务调度对象

winrt::Windows::System::DispatcherQueue dq 是 C++/WinRT 中用于管理线程任务调度的核心类,它管理按优先级排列的任务队列,确保任务在线程上以串行方式执行。这个类型的对象有以下特点:

  • 每个线程最多拥有一个 DispatcherQueue
  • 可以向该线程投递异步任务
  • 任务按优先级排队执行
  • 依赖线程消息循环(Message Loop)

在应用启动的时候,我们就创建了这个dq对象,它管理着UI线程的任务队列

cpp 复制代码
winrt::Windows::System::DispatcherQueue dq;

dq{ winrt::Windows::System::DispatcherQueue::GetForCurrentThread() }

在WinUI3或者UWP项目中,通过上述代码就可以直接得到dq。

传统的Win32项目,必须要提前执行如下逻辑,才能得到dq。(DispatcherQueue 不是 Win32 线程的默认组成部分)

cpp 复制代码
winrt::init_apartment(winrt::apartment_type::single_threaded);
DispatcherQueueOptions options{ sizeof(DispatcherQueueOptions),DQTYPE_THREAD_CURRENT,DQTAT_COM_STA };
static winrt::Windows::System::DispatcherQueueController controller{ nullptr };
auto hr = CreateDispatcherQueueController(options,
     reinterpret_cast<ABI::Windows::System::IDispatcherQueueController**>(winrt::put_abi(controller)));

不要掐死UI线程

假设我们有如下异步代码,用于获取剪切板内的文本。

cpp 复制代码
IAsyncOperation<winrt::hstring> Util::getTextFromClipboard()
{
    auto view = Clipboard::GetContent();
    if (view.Contains(StandardDataFormats::Text()))
    {
        co_return co_await view.GetTextAsync();
    }
    co_return L"";
}

如果你通过如下方法获得剪切板内的文本

cpp 复制代码
auto str = getTextFromClipboard().get();

此时可能会报如下错误:

这是因为:

  1. .get() 阻塞当前线程等待异步完成
  2. 异步操作内部需要调度到UI线程执行 Clipboard API(操作剪切板的API的要求)
  3. 但当前UI线程被你阻塞了(.get() 没返回)
  4. 消息循环不运转,无法调度
  5. WinRT 检测到"该UI线程无法响应" 则抛出 !is_sta_thread

解决方案就是不要掐死UI线程

cpp 复制代码
auto strOp = Util::getTextFromClipboard2();
strOp.Completed([this](auto const& sender, auto status) {
   auto str = sender.GetResults();
});

或者把这段逻辑也包在IAsyncAction方法中,使用co_await调用getTextFromClipboard方法。

相关推荐
知识分享小能手4 小时前
C++ 学习教程,从入门到精通,C++ 入门知识 — 完整知识点(1)
开发语言·c++·学习
qq_199886874 小时前
第8板块·第2节:统一内存的高级特性与性能调优
c++·人工智能·gpu算力·cuda
2401_839080545 小时前
C++常见八股
数据结构·c++·算法
qq_199886876 小时前
第6板块 第2节 CUDA运行时API与驱动API 核心笔记
c++·人工智能·gpu算力
syagain_zsx7 小时前
算法基础篇 · 03 枚举(C++ 题解)
c++·算法·二进制·枚举
weixin_307779137 小时前
C++代码实现MATLAB中的ode23tb函数功能
开发语言·c++·算法·matlab
S_WEAN7 小时前
C++STL - string类的模拟实现
c++
S_WEAN8 小时前
【C++】STL - string类
c++
码匠许师傅8 小时前
【C++三方组件】toml++:人性化的配置文件格式
c++