如何为非WinRT类型实现Windows线程池调度的协程支持
非WinRT类型协程对接Windows线程池方案参考
线程池选型判断
- 若项目仅面向Windows平台、无跨平台异步需求,优先选择将
cppcoro::task<T>或libunifex::task<T>对接Windows原生线程池:原生线程池是系统内置组件,默认对接进程IO完成端口、适配系统全局资源调度策略,无额外第三方运行时依赖。适配成本极低,只需要实现对应库要求的scheduler概念,内部封装PTP_WORK提交逻辑即可,常规场景下适配代码量不到200行,不需要修改任务本身的协程状态机实现。 - 若项目已经全量引入cppcoro/libunifex、存在跨平台编译需求,直接使用库内置的线程池实现性价比更高:两个库的内置线程池都实现了无锁任务队列、工作窃取等优化,实际运行性能和Windows原生线程池差距在5%以内,还省去了自行维护调度适配层的成本。注意不要在同一个进程内混用两套独立线程池,否则容易出现线程数无意义膨胀、调度优先级错乱、跨池任务同步开销陡增的问题。
C++/WinRT IAsyncOperation<TResult> 代码说明
你看到的这段代码并不是IAsyncOperation的完整实现,只是C++/WinRT自动生成的ABI投影包装壳,所有核心逻辑都封装在基类模板中:
template <typename TResult> struct __declspec(empty_bases) IAsyncOperation : winrt::Windows::Foundation::IInspectable, impl::consume_t<winrt::Windows::Foundation::IAsyncOperation<TResult>>, impl::require<winrt::Windows::Foundation::IAsyncOperation<TResult>, winrt::Windows::Foundation::IAsyncInfo> { static_assert(impl::has_category_v<TResult>, "TResult must be WinRT type."); IAsyncOperation(std::nullptr_t = nullptr) noexcept {} IAsyncOperation(void* ptr, take_ownership_from_abi_t) noexcept : winrt::Windows::Foundation::IInspectable(ptr, take_ownership_from_abi) {} };
逐部分拆解逻辑:
__declspec(empty_bases)是MSVC专属编译优化,提示编译器对空基类应用空基类优化,避免多个无成员变量的模板基类占用额外的对象内存空间。- 第一层基类
winrt::Windows::Foundation::IInspectable是所有WinRT类型的根接口,封装了ABI层的引用计数、接口查询、跨套间封送逻辑。两个构造函数的作用分别是创建空实例、直接从原始ABI接口指针接管所有权构造包装对象,take_ownership_from_abi_t参数是用于重载区分的标签,标记传入的指针已经持有对应引用计数,构造时不需要额外调用AddRef。 impl::consume_t<...>是C++/WinRT的方法生成层,会根据接口的元数据定义自动生成类型安全的C++包装方法,你平时调用的Completed回调注册、GetResults取值等方法都是这个模板自动生成的,不需要手写。impl::require<...是编译期约束,强制要求IAsyncOperation接口必须实现IAsyncInfo接口(提供状态查询、错误码获取、取消操作等通用能力),如果底层ABI实现不满足该约束会直接触发编译错误,不需要运行时做QI检查。- 开头的
static_assert是入参类型校验,确保传入的TResult符合WinRT类型规范(基础类型、WinRT接口、符合规则的值类型),禁止直接传入非WinRT的自定义C++类型。
真正的协程对接、完成通知触发、线程池调度逻辑都不在这个投影壳里:一部分在winrt/impl/coroutine相关头文件的协程promise实现中,一部分在运行时层的COM接口实现中,单独看这个包装类自然找不到核心逻辑。
源码梳理建议
Kenny Kerr在CppCon 2016的演讲把C++/WinRT的投影原理、协程对接逻辑讲得非常透彻,适合作为入门参考。梳理源码时不需要死磕自动生成的投影类,直接看这几个部分效率更高:
winrt/Windows/Foundation/impl目录下的异步接口实现,能找到完成回调注册、结果返回的具体逻辑winrt/impl目录下的协程相关头文件,里面是promise_type、await_suspend、await_resume的具体实现,可以直接看到WinRT异步操作和C++协程机制的对接流程- 如果你要实现非WinRT类型的协程支持,完全不需要照搬C++/WinRT那套COM引用计数、ABI兼容的逻辑,只需要把协程挂起后的恢复回调提交到Windows线程池执行即可,实现比C++/WinRT的原生逻辑轻量很多。
内容的提问来源于stack exchange,提问作者Tom Huntington
相关产品推荐
相关产品推荐

