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方法。

相关推荐
王老师青少年编程2 小时前
2020年信奥赛C++提高组csp-s初赛真题及答案解析(阅读程序第1题)
c++·题解·真题·初赛·信奥赛·csp-s·提高组
会周易的程序员2 小时前
cNetgate插件架构设计详解 动态库 脚本二开lua, python, javascript
javascript·c++·python·物联网·lua·iot
普通网友2 小时前
C++与Rust交互编程
开发语言·c++·算法
xyty33203 小时前
QImageReader 的全局静态锁原理
c++·windows·qt
散峰而望3 小时前
【算法竞赛】堆和 priority_queue
开发语言·数据结构·c++·算法·贪心算法·动态规划·推荐算法
adore.9683 小时前
2.20 oj83+84+85
c++·复试上机
alexwang2113 小时前
B2007 A + B 问题 题解
c++·算法·题解·洛谷
Bruce_Liuxiaowei3 小时前
深入剖析 Windows 网络服务:用 witr 一键溯源所有监听端口
windows·安全·系统安全
Zik----4 小时前
Leetcode2 —— 链表两数相加
数据结构·c++·leetcode·链表·蓝桥杯