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的支持/使用,因此我在这里不再赘述。

相关推荐
故事不长丨6 小时前
C#正则表达式完全攻略:从基础到实战的全场景应用指南
开发语言·正则表达式·c#·regex
哈库纳玛塔塔7 小时前
放弃 MyBatis,拥抱新一代 Java 数据访问库
java·开发语言·数据库·mybatis·orm·dbvisitor
phltxy8 小时前
从零入门JavaScript:基础语法全解析
开发语言·javascript
天“码”行空8 小时前
java面向对象的三大特性之一多态
java·开发语言·jvm
odoo中国9 小时前
Odoo 19 模块结构概述
开发语言·python·module·odoo·核心组件·py文件按
代码N年归来仍是新手村成员10 小时前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
Z1Jxxx10 小时前
01序列01序列
开发语言·c++·算法
沐知全栈开发10 小时前
C语言中的强制类型转换
开发语言
关于不上作者榜就原神启动那件事11 小时前
Java中大量数据Excel导入导出的实现方案
java·开发语言·excel
坚定学代码11 小时前
基于观察者模式的ISO C++信号槽实现
开发语言·c++·观察者模式·ai