特性完成:VS2019 v16.8全面支持C++协程

特性完成:VS2019 v16.8全面支持C++协程

作者:BlogUpdater |  时间:2020-09-18 |  浏览:107 |  评论已关闭 条评论

一段往事
回首有关C++协程的往事,真是犹如一段十分漫长的旅程。
早在2013年,我们就官宣了一个有关”可恢复函数”的早期预览版本,接下来在2014年,我们添加了/await开关并提交了C++标准化建议的初始版本。2015年,我们提交了修订版本。后来,通过Visual Studio 2017和Visual Studio 2019,我们都在持续地跟踪有关协程TS(Technical Sepecificaion)的内容更新。2019年,随着协程完全实现被纳入到C++标准中,我们终于可以宣布了:Visual Studio 2019 v16.8将完全支持C++20协程特性。

Standard vs. TS Coroutines
最终通过标准化程序成为C++20一部分的协程支持,与早期的提议草案以及我们在/await开关下在MSVC中获得的实验版本协程有所不同。
我们在实现v16.8中的协程支持时,考虑了两个重要,但是相互矛盾的目标。
1. 提供一个严格遵循C++标准的协程实现,使得用户可以编写和使用可移植代码。
2. 确保那些使用实验版本协程的用户可以毫不费力地升级到v16.8而无需改动他们的代码。

随着提案的更改,我们会尽可能增加新的支持,而不破坏针使用早期协程版本的代码。
这当然不是标准的:它仍然接受所有旧的关键字,名称和签名,和上面的第一个目标对应。
与我们在/await下实现的原始版本相比,还存在少量的行为更改,例如一个promise对象的构建方式。这些可能导致先前可以通过编译的程序变得无法编译或表现出与之前不一样的行为。

Standard Mode – /std:c++latest
使用比C++17更新的编译器语言版本模式时,就能实现对C++20协程的支持,同时,不带有对旧版TS的支持。现在,这是/std:c++latest,在添加了C ++ 17之后,它将继续进入编号版本的开关。使用这种语言开关进行编译且不带/await时,你就可以获得对C++20协程的严格支持,我们在头文件中和std名称空间中提供了库级别支持。此模式将针对早期建议中的非标准代码会给出错误提示,例如,使用了bare await关键字或返回bool的initial_suspend函数,并且仅在其与早期实现不同时才支持这种标准行为。

Extension Mode – /await
协程的早期使用者可以继续使用/await开关和任何语言版本的开关(包括/std:c++latest)来编译其非标准化的代码,并继续使用实验性头文件和命名空间。只要不破坏兼容性,我们都会在此模式下添加那些缺少的标准功能并提供错误修复。

我们建议现有的协程使用者尽快地迁移到标准版协程,新用户应优先使用标准模式而不是/await。 针对现有用户,我们将继续支持/await开关,但是将来的协程开发版本将处于标准模式,并且在那里将实现新功能。除了一些极端的情况,将项目从/await迁移到C++20应该是一个简单的过程。

Visual Studio 2019 v16.8 都有那些新东西?
v16.8版本中引入了一些有关协程的新功能和改进:

> 对称转移(Symmetric transfer)
> 无操作协程(No-op coroutines)
> 协程Promise构造函数参数
> 定义良好的行为,使异常控制流从协程中退出
> 标准返回对象转换行为
> 优化后的调试体验
> 通用帧布局,以提高与其他供应商的兼容性
> 大量错误修复

无操作协程和大多数错误修复程序也已在/await下实现,但大多数这些更改仅在以标准模式构建时可用。在本文的其余部分,我们将仔细研究其中一些新功能的细节以及Visual Studio有关协程的下一个阶段版本。

对称转移和无操作协程
这是C++20协程支持的最后两个缺失的部分。对于对称转移,协程可以指示协程句柄,以便另一个协程在挂起时立即恢复。这是通过使用coroutine_handle的返回类型定义协程await_suspend函数来完成的,如下图所示:

在标准模式下,此挂起和恢复操作可以在不将另一个帧引入到调用堆栈的情况下工作。这样就可以在协程之间进行不限大小的数据传输,而不会冒堆栈溢出的风险。

优化后的调试体验
v16.8版引入了一些用于协程的新调试功能。
修正了协程步入的一些问题,尤其是和选项”Just My Code”一起工作的时候。
你还可以在协程程序中展开栈指针。通过这个特性,你可以看到一些公开的数据,例如协程参数值和promise类型的成员(仅限标准协程)。
我们还更改了许多由编译器生成的符号的名称,以更好地与调试器的表达式求值一起使用。有了它们,就可以更容易在即时窗口或监视窗口中使用,也可以作为条件断点使用。如下图所示:

通用帧布局
在标准C++ 20模式下,协程框架有了新的内部表示形式。这暴露了对协程非常重要的框架部分,例如如何以厂商之间通用的方式恢复或销毁它。一个供应商生产的目标文件或库中生成的协程可能会被另一供应商使用。这并不意味着完整框架的布局在各供应商之间是通用的,甚至不能保证在编译器版本之间是稳定的,但是它确实标准化了(尽管是非正式的)标准库类型std::coroutine_handle与基础协程框架对象之间的接口,并在从库中公开或使用协程时应有助于提高兼容性和灵活性。我们还引入了在Clang使用相同内置函数的支持,从而实现了更好的头文件级兼容性。

当前,不同编译器厂商之间的协程支持级别有所不同,但大部分正在不断提高。随着C++20支持在各个编译器中广泛推广,我们希望这将变得更加有用和重要。我们致力于为协程提供通用,稳定的ABI,以使不同版本之间的接口尽可能实现无缝升级。

接下来
C++20中的协程还存在一些限制。虽然已经采用了核心语言功能,但是标准库中没有真正的协程支持。好消息是,我们预计很快就会有改变,在下一个C++语言版本中将对协程提供更广泛的库支持。

我们针对C++ 20协程的下一阶段的目标是不断改善调试体验。这种情况的一个方面是,这是一个更自然的趋势,使开发者可以更容易跟踪协程执行,就好像它是普通的同步功能一样。我们还在寻找改进的协程句柄可视化功能,以实现轻松查看协程运行状态。

总结
协程来了,老哥们是否准备好了呢?
还是,和我一样,继续写写HelloWorld,混混日子?

最后
Microsoft Visual C++团队的博客是我非常喜欢的博客之一,里面有很多关于Visual C++的知识和最新开发进展。大浪淘沙,如果你对Visual C++这门古老的技术还是那么感兴趣,则可以经常去他们那(或者我这)逛逛。
本文来自:《C++ Coroutines in Visual Studio 2019 Version 16.8》

标签:

评论已关闭。