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

2024-02-04 01:36
文章标签 c++ 介绍 qt 20 协程库 qcoro

本文主要是介绍QCoro: Qt C++ 20 协程库介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

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

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

例子

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

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

传统信号槽:

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操作,并保持事件循环。

协程:

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

这篇关于QCoro: Qt C++ 20 协程库介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/676018

相关文章

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

QT Creator配置Kit的实现示例

《QTCreator配置Kit的实现示例》本文主要介绍了使用Qt5.12.12与VS2022时,因MSVC编译器版本不匹配及WindowsSDK缺失导致配置错误的问题解决,感兴趣的可以了解一下... 目录0、背景:qt5.12.12+vs2022一、症状:二、原因:(可以跳过,直奔后面的解决方法)三、解决方

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用