如果C#和JavaScript允许我多次等待Windows Runtime异步操作,为什么C++/WinRT不行?
摘要
Raymond Chen解释了为什么C++/WinRT不像C#、JavaScript和Python那样允许多次等待异步操作,其原因是没有标准库的task类型,以及不为你未使用的功能付费的原则。
<p>Windows Runtime通过类似<code>IAsyncOperation</code>的类型来表达异步执行。不同的语言选择以不同的方式来表示这一概念。</p>
<ul>
<li>C#将其包装在<code>System.<wbr />Threading.<wbr />Tasks.<wbr />Task</code>中。</li>
<li>JavaScript将其包装在<code>Promise</code>中。</li>
<li>Python将其包装在<code>asyncio.<wbr />Future</code>中。</li>
</ul>
<p>上述所有包装器都支持多个延续任务,这意味着你可以多次调用<code>await</code>。</p>
<p>但C++/WinRT并没有提供这样的包装器。C++/WinRT只是暴露了原始的<code>IAsyncOperation</code>。<code>IAsyncOperation</code>不允许附加多个延续任务,因此你不能多次<code>co_await</code>它。</p>
<p>唉。</p>
<p>为什么C++/WinRT不跟上潮流,提供一个支持多个延续任务的包装器呢?</p>
<p>嗯,一个原因是C#的<code>System.<wbr />Threading.<wbr />Tasks.<wbr />Task</code>、JavaScript的<code>Promise</code>和Python的<code>asyncio.<wbr />Future</code>都是各自语言标准库的一部分。这使得它们成为投影的明显选择,因为它们以一种任何使用该语言编写代码的人都普遍理解的方式来表示异步执行。此外,它们是标准库特性这一事实使得投影可以建立在其他人的工作之上:标准库的维护者已经实现、测试并优化了这些类。</p>
<p>C++没有用于异步执行的标准库。相反,C++提供了原材料,你可以用这些材料编写自己的库。而我们早在<a title="三大协程系列指南" href="https://devblogs.microsoft.com/oldnewthing/20210504-01/?p=105178">学习C++协程时</a>就编写了这样一个库,并实现了一个<code>simple_task</code>类。</p>
<p>缺少标准库的<code>task</code>类型意味着没有预制的包装器可供C++投影利用。那就只能各自为战了。</p>
<p>C++/WinRT为<code>IAsyncOperation</code>提供了一个最小的协程promise,这是出于C++的一般原则:“不为你不用的功能付费。”绝大多数情况下,你不需要多次等待一个<code>IAsyncOperation</code>,那么为什么让每个<code>IAsyncOperation</code>都为此付出代价呢?</p>
<p>下次,我们将探讨一个你可能想要多次等待<code>IAsyncOperation</code>的情况,并看看有什么方法可以绕过C++/WinRT的限制。</p>
<p><b>额外闲聊</b>:确实有一个现成的库支持多个延续任务:并行模式库<code>concurrency::<wbr />task</code>对象。较旧的C++/CX投影本身并不包装<code>IAsyncOperation^</code>,但并行模式库确实对C++/CX的<code>IAsyncOperation^</code>有特殊了解,因此你可以将<code>IAsyncOperation^</code>包装在PPL的<code>task</code>中,并多次<code>co_await</code>这个<code>task</code>。</p>
<p>所以我想你可以说已经有人为你写好了包装器。不幸的是,这个包装器相当庞大,因为PPL库有<i>太多功能</i>,其中大部分你可能都没用到。可以说,C++/WinRT试图做到极简,而PPL则试图做到极致。从小做起再逐步增长,比一开始就大而全再试图精简要容易得多。(而且C++/WinRT通常将“增长”部分委托给Windows实现库,该库通过类似<tt>cppwinrt_helpers.h</tt>和<tt>cppwinrt_authoring.h</tt>的头文件添加额外功能。)</p>
<p>本文<a href="https://devblogs.microsoft.com/oldnewthing/20260526-00/?p=112354">如果C#和JavaScript允许我多次等待Windows Runtime异步操作,为什么C++/WinRT不行?</a>首发于<a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>。</p>
查看缓存全文
缓存时间: 2026/05/27 08:52
# 如果C#和JavaScript允许我多次await一个Windows Runtime异步操作,为什么C++/WinRT不行? - The Old New Thing
来源:https://devblogs.microsoft.com/oldnewthing/20260526-00?p=112354
Windows Runtime通过`IAsyncOperation`等类型来表达异步执行。不同语言选择不同的方式来表示这一概念。
- C# 将其包装在 `System.Threading.Tasks.Task` 中。
- JavaScript 将其包装在 `Promise` 中。
- Python 将其包装在 `asyncio.Future` 中。
上述所有包装器都支持多个延续(continuations),这意味着你可以多次对它们使用 `await`。
但 C++/WinRT 并没有提供这样的包装器。C++/WinRT 直接暴露原始的 `IAsyncOperation`。`IAsyncOperation` 不允许附加多个延续,因此你不能多次对它使用 `co_await`。
糟糕。
为什么 C++/WinRT 不与时俱进,提供一个支持多个延续的包装器呢?
嗯,一个原因是 C# 的 `System.Threading.Tasks.Task`、JavaScript 的 `Promise` 和 Python 的 `asyncio.Future` 都是各自语言标准库的一部分。这使得它们成为投影(projection)的显而易见的选择,因为它们是表示异步执行的方式,任何使用该语言编写代码的人都能普遍理解。此外,它们是标准库特性,这使得投影可以借助他人的工作:标准库维护者已经实现、测试并优化了这些类。
C++ 没有一个用于异步执行的标准库。相反,C++ 提供了原始材料,你可以用它们编写自己的库。我们曾写过这样一个库,当时我们学习了 C++ 协程(https://devblogs.microsoft.com/oldnewthing/20210504-01/?p=105178)并实现了一个 `simple_task` 类。
缺乏标准库的 `task` 类型意味着没有现成的包装器可供 C++ 投影利用。各人自扫门前雪。
C++/WinRT 为 `IAsyncOperation` 提供了一个最小的协程承诺(promise),遵循 C++ 的通用原则“不为未使用的东西付费”。绝大多数情况下,你不需要多次 await 一个 `IAsyncOperation`,那么为什么让每个 `IAsyncOperation` 都为此付出代价呢?
下次,我们将看一个需要多次 await `IAsyncOperation` 的案例,并探讨如何绕过 C++/WinRT 的限制。
**额外闲聊**:确实存在一个支持多个延续的现有库:并行模式库(Parallel Patterns Library)的 `concurrency::task` 对象。较旧的 C++/CX 投影本身并不包装 `IAsyncOperation^`,但并行模式库确实对 C++/CX 的 `IAsyncOperation^` 有特殊支持,因此你可以将 `IAsyncOperation^` 包装在 PPL 的 `task` 中,并多次对 `task` 使用 `co_await`。
所以我想你可以说已经有人为你写好了包装器。不幸的是,这个包装器相当庞大,因为 PPL 库拥有*太多功能*,其中大部分你可能都不会用到。可以说,C++/WinRT 追求极简主义,而 PPL 追求极繁主义。从小处着手并逐渐扩展,比从一开始就庞大而后试图精简更容易。(而 C++/WinRT 通常将“扩展”部分委托给 Windows 实现库,该库通过 `cppwinrt_helpers.h` 和 `cppwinrt_authoring.h` 等头文件添加额外功能。)
### 分类
### 主题
## 作者
Raymond Chen
Raymond 参与 Windows 的发展已有 30 多年。2003 年,他创立了一个名为 The Old New Thing 的网站,该网站的人气增长远超他最疯狂的想象,这一发展至今仍让他感到不安。该网站后来出版了一本书,巧合的是书名也是 The Old New Thing(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些不传达有用信息的故事。
相似文章
在多个协程之间共享单个Windows Runtime IAsyncOperation的结果,第3部分
本文讨论了一个C++/WinRT模式,用于缓存Windows Runtime IAsyncOperation的结果,包括处理失败的情况,以便多个协程可以共享缓存的结果或异常。
ClojureScript 迎来 Async/Await
ClojureScript 1.12.145 通过 ^:async 提示引入原生异步函数支持,实现与 JavaScript async/await 的直接互操作,无需额外依赖。
如何在Windows运行时中使用Win32结构?
本文解释了如何通过声明具有相同布局的影子结构在Windows运行时中使用Win32结构,包括具体示例和常见结构的替代方案。
Theseus: 将 win32 翻译为 wasm
将 Windows 可执行文件 (win32/x86) 翻译为 WebAssembly 以在浏览器中运行,讨论诸如阻塞与异步设计等挑战。
Rust异步与ARM通用定时器
一篇技术博客文章,探讨了在ARM架构上使用ARM通用定时器进行Rust异步编程,比较了定时器外设,并讨论了Embassy和RTIC等框架。