使用期望值的函数式编程
函数式编程(Functional Programming)是一种编程范式。在函数式编程中,函数被视为第一类对象,可以作为参数传递给其他函数,也可以作为返回值返回。函数式编程的核心思想是避免可变状态和副作用,强调使用纯函数进行计算。纯函数是指输入相同,输出也必定相同,并且没有任何副作用的函数。这种特性使得函数式编程更容易进行推理和测试,也更容易实现并发编程。
在C++中,期望值对象可以在线程间互相传递, 并允许计算结果依赖于另外一个,而非对共享数据的显式访问——这使函数式编程模式并发化(FP-style concurrency)的实现变得容易。
首先来看一个FP模式版的快速排序。

在创建new_lower和new_higher时,std::move用于将lower_part和input的所有权转移到递归调用的函数中。通过这种方式,可以避免对这些对象进行拷贝操作,而只是在递归调用中使用原始对象的资源。当传递一个对象作为参数进行函数调用时,通常会触发拷贝构造函数或移动构造函数。使用std::move可以明确指示我们希望转移对象的所有权,而不进行拷贝构造。这对于容器等需要大量数据移动的对象来说,可以带来显著的性能提升。
FP模式中,期望值很容易转化为并行版本。下面是FP模式线程强化版的快速排序。

这里最大的变化是,当前线程不对小于中间值部分的列表进行排序,使用std::async在另一线程对其进行排序。大于部分则和之前一样,使用递归的方式进行排序。通过递归调用parallel_quick_sort,就可以利用硬件并发了。std::async会启动一个新线程,当递归三次时,就会有八个线程在运行。
在这里,每个递归调用都创建了一个新的线程,并且通过异步任务进行并发计算。每个线程独立地操作自己的数据,没有共享的可变数据,避免了线程之间的竞争条件和同步问题,减少了并发编程中常见的错误和复杂性。尽管线程之间没有共享的可变数据,但是通过通信通道(std::future)来传递数据和结果。这种通信方式允许信息在不同线程之间传递,实现了线程之间的协作。