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

相关推荐
liu-yonggang13 小时前
MISRA C++:2008 — Guidelines for the use of the C++ language in critical systems
c++
凡人叶枫13 小时前
Effective C++ 条款05:了解 C++ 默默编写并调用哪些函数
java·linux·开发语言·c++·effective c++·编程范式
少司府13 小时前
C++进阶:AVL树
开发语言·数据结构·c++·二叉树·avl树
挨代码13 小时前
UEC++ 数据类型及继承关系
c++·ue
超级无敌zhq13 小时前
内网权限维持实战:打造持久化后门与隐蔽通道
网络·windows·安全·网络安全
代码中介商13 小时前
C++11右值引用与移动语义深度解析
开发语言·c++
码上有光13 小时前
c++:二叉搜索树(map和set的底层结构)
开发语言·c++·递归·二叉搜索树
Brilliantwxx13 小时前
【C++】 链式哈希表(Separate Chaining)
c++·哈希算法·散列表
shandianchengzi13 小时前
【记录】VSCode|Windows 下 VS Code 配置 Git Bash 为默认终端完整教程
windows·git·vscode·bash
大白话_NOI14 小时前
【洛谷 P1480】A/B Problem(高精度除法 Ⅰ)详细题解
c++