QCoro: Qt C++ 20 协程库介绍

C++20 推出了协程的实现(coroutines)。虽然开发一个支持协程特性的类库还是要花很多功夫的,但是使用一个开发好的类库则是非常嗨。这也是C++ 委员会一贯的原则:

如果你是类库开发者,必须足够有耐心学习拗口的特性。但如果是类库使用者,则直接吃语法糖爽歪歪就行了

协程是一种线程内的快速执行序切换功能,比回调函数调用起来要简单。这篇文章介绍了协程的基本概念。

QCoro 是 Qt 的一个协程库,利用C++20的特性,可以显著简化以前用信号-槽回调才能完成的操作。代码可从github下载。

例子

QCoro库提供了一组工具来使用C++20与Qt的协同程序。下面的例子演示了下载一些网络数据的实现对比(传统信号槽和协程)。

以前使用Lambda槽回调时,为了生存周期,需要显式new Qt对象,并deleteLater。

传统信号槽:

cpp 复制代码
void MyClass::fetchData() {
    auto *nam = new QNetworkAccessManager(this);
    auto *reply = nam->get(QUrl{QStringLiteral("https://.../api/fetch")});
    QObject::connect(reply, &QNetworkReply::finished,
                     [reply, nam]() {
                         const auto data = reply->readAll();
                         doSomethingWithData(data);
                         reply->deleteLater();
                         nam->deleteLater();
                     });
}

现在只要稍微注意一下上下文的生存周期,就可以直接用栈上的局部变量调用IO操作,并保持事件循环。

协程:

cpp 复制代码
QCoro::Task<> MyClass::fetchData() {
    QNetworkReply nam;
    auto *reply = co_await nam.get(QUrl{QStringLiteral("https://.../api/fetch")});
    const auto data = reply->readAll();
    reply->deleteLater();
    doSomethingWithData(data);
}

这里的神奇之处在于co-await关键字,它将方法fetchData()变成了一个协程,并在网络请求运行时暂停了它的执行。当请求完成时,协程将从暂停的位置恢复并继续。注意!当co_await 暂停时,Qt事件循环照常运行!

官网的解释

下面的段落试图以简单的方式解释什么是协同程序以及co_await做什么。我不能保证这些都是事实上正确的。有关更详细(和正确)的信息,请参阅本文底部链接的文章。

简单地说,Coroutine类似于普通函数,只是它们可以在中间挂起(和恢复)。当协同程序被挂起时,执行返回到调用了协同程序的函数。如果该函数也是协同程序,并且正在等待(co_awaiting)当前协同程序完成,则它也将被挂起,执行将返回到调用该协同程序的函数,依此类推,直到到达作为实际函数(而不是协同程序)的函数。在常规Qt程序的情况下,这个"顶级"非协同程序函数将是Qt的事件循环------这意味着当从Qt事件循环调用协同程序时,当您的协同程序被挂起时,Qt事件环将继续运行,直到再次恢复协同程序。

在许多其他事情中,这允许您编写异步代码,就像它是同步的一样,而不会阻塞Qt事件循环,并使应用程序无响应。请参阅本文档中的不同示例。

现在让我们看看co_await关键字。这个关键字告诉编译器,这是协同程序希望挂起的点,直到等待的对象(awaitable)准备就绪。任何类型都可以是可等待的------要么是因为它直接实现C++协同程序机械所需的接口,要么是因为提供了一些外部工具(如该库)来将该类型包装为实现可等待接口的东西。

C++协同程序引入了两个额外的关键字-co_return和co_yield:

从应用程序程序员的角度来看,co_return的行为与return完全相同,只是您不能在协同程序中使用常规return。然而,在引擎盖下有一些主要的区别,这可能是为什么有一个用于从协同程序返回的特殊关键字的原因。

coyield允许协同程序在不实际返回的情况下生成结果。可以用于写入生成器。目前,这个库没有对co_yield的支持/使用,因此我在这里不再赘述。

相关推荐
Cc.Y3 小时前
Java零基础入门:字符串深度掌握——从基础API到StringBuilder性能优化
java·开发语言·性能优化
java资料站7 小时前
十一、评估测试
开发语言·人工智能·python
左手の明天8 小时前
SLIP 协议封装与解析详解:原理、C 代码与实战案例
linux·c语言·开发语言·c++
海上小飞龙8 小时前
【KMP算法-下篇】同一道题:Java 库函数 2600 微秒,手写 KMP 16 微秒
java·开发语言·算法
朝朝辞暮i8 小时前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
狗凯之家源码网8 小时前
淘客口令落地页生成器 PHP 版深度评测
开发语言·php
画绛集美术8 小时前
本地化部署LLM用于美术史问答:一间美术教室的实践记录
开发语言·php
八解毒剂9 小时前
【计组】中央处理器CPU
java·开发语言·计算机组成原理
FakeOccupational10 小时前
【电路笔记 信号】DBPSK 波形查找表+脉冲成形(升余弦+根升余弦滤波)
开发语言·笔记
打工仔折腾 AI10 小时前
工业场景下时序库与实时计算一体化架构选型实践对比
java·开发语言·后端·python·性能优化·架构·ai agent 实战