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

相关推荐
「QT(C++)开发工程师」7 小时前
C++ auto 用法详解
开发语言·c++
OPEN-F7 小时前
C++STL教程:容器适配器与实用工具
开发语言·c++
OPEN-F7 小时前
C++模板教程:变参模板、折叠表达式与SFINAE
java·开发语言·c++
有点。7 小时前
C++二叉搜索树进阶
开发语言·c++
HugoStudio_SWAN7 小时前
【擦除重绘】C++ 控制台动画:弹跳 Logo DVD 屏保效果
开发语言·c++·学习·程序人生
kyle~9 小时前
C++_STL---迭代器失效
开发语言·c++
wuminyu10 小时前
深入剖析 Panama Off-heap 的性能损耗与开销
java·linux·c语言·jvm·c++
rhythm-ring11 小时前
宏定义续行符 \ 的使用与踩坑
c语言·c++
江安下小雨12 小时前
muduo网络库(十六):新增连接池模块
网络·c++
「QT(C++)开发工程师」12 小时前
C++11 std::unique_ptr — 独占所有权的智能指针
c语言·开发语言·c++·qt