一、并行库
在前面的文章中分析过不少的并行库(OpenMp),包括DPDK这种框架中的并行处理方式。其实对于开发者来说,所谓并行库的出现,就是在对单核CPU的处理达到上限后的一种分布式的解决方法,这种分布式,可能是大家认知中的多台计算机,也可能是在一个CPU中布置多个核。它们面临的主要的问题或者说核心的问题,基本都一样,解决问题的原理也差不多。
既然是哥儿几个打架,那么如何把哥儿几个的整体能力发挥到最大才是重点。而在这些并行库的核心处理思想就是这样,即资源的分配、管理和调试以及如何与上层应用实现异步的协调工作。
二、C++26中的应对
C++26中对并行库的支持做了进一步的扩展,一个是增强了std::execution的功能,另外一个是对异步编程进行了类似其它语言的形制泛化。
在前面的“c++26新功能——std::execution”中,已经对execution进行了基础的介绍,现在看一看其较高级的应用:
1、支持对CPU(核心)的绑定(CPU的亲和性)
这个是所有并行库的标准操作了,这样做的目的也很明显,就是为了充分的利用特定场景下的核心操作行为,减少核心切换带来的效率的损失。这个在前面的DPDK系列和Linux编程系列中都有过分析,有兴趣可以回头看一看。
#include <execution>
auto snd = std::execution::on(
owner_scheduler, // 自定义的目标执行上下文
std::execution::just(100) // 输入发送器
);
2、支持工作任务的优先级队列
大家都知道,虽然在Windows或Linux等操作系统上都有优先级的指定,但实际上,都只是一种标准的指定,达不到开发者的应用需求。底层环境很难重定义,那么就只能在上层的并行应用库中进行处理。通过不同的任务队列来实现复杂的任务处理场景。不过,这个优先级队列仍然需要系统的API支持(也就是说,优先级精细的级别仍然无法确定)。在网上有一些相关应用的报道,一些大公司在使用其后,效率提高极其明显。
另外一个是异步编程的扩展支持即链式组合操作,这个在其它语言如Kotlin、C#等语言开发者中都经常遇到。其实正如前面所述,如果真正的协程达到如现在线程应用一样方便简单后,异步编程可能就不会再如此的重要了。当然,不重要就意味着很多所谓的高手就又失去了一个核心竞争力的编程领域。
template <typename F>
auto then(F&& func) -> std::future<std::invoke_result_t<F, std::future<T>&>>;
其实很好理解,就是当future完成后会自动触发后面的then操作,有过链式开发经验或函数编程经验的会非常容易理解。如果在开发中使用设计模式中的复杂一些的构建者模式、责任链模式以及观查者模式等都会明白这种操作的过程。
三、例程
还是那句话,这些例子都是一些网上或草案文档中的例子,未必是最终版本,大家只做一下参考即可,不必纠结:
//调度器
auto mySched = std::execution::thread_pool_scheduler();
//指定线程池执行
std::execution::on(
mySched,
std::execution::just()
| then([] { /* Process work */ })
).submit([] { std::cout << "OK!"<<std::endl; });
//链式操作
#include <future>
#include <iostream>
std::future<int> asyncDoWith(int a) {
return std::async([a]{ return a++; });
}
int main() {
asyncDoWith(1)
.then([](std::future<int>& f) {
int v = f.get();
std::cout << "first then: " << v << std::endl;
return v + 1;
})
.then([](std::future<int>& f) {
int v = f.get();
std::cout << "second then: " << v << std::endl;
return std::to_string(v);
})
.then([](std::future<std::string>& f) {
std::cout << "end: " << f.get() << std::endl;
}).wait();
}
上面的代码在某些编译器上可能已经有较好的支持,但大多数的在用的编译器一般都不支持,这个大家一定要清楚。
四、总结
其实并行库作为C++标准重要的一环,一定是在未来几个新标准中都会不断的增加和完善相关细节的。但这种发展一定是一个反复否定的过程,所以上面给大家分析的,可能未必是最终版本的,但也可能在未来版本又出现了。学习新标准重点是把握一些方向性和趋势性的东西。而实际的应用一定要等待新标准正式落地后,更确切的说明是主流的编译器支持新标准后,才建议在生产环境中大规模的应用。