考虑一个算法,代码如下:
#include <algorithm> void process(int& x){ //do some calculate } int main() { int arr[100000]; std::for_each( arr, arr+100000, [](auto & x){ process(x); }); }
for_each对于每一个数组arr的成员,都去调用了process(int& x)。这个过程是单线程(main所在的主线程)进行的。
如果想增加性能,让多线程并行的处理,需要做一些改造。例如,创建线程1处理0-30000项,创建线程2处理30001-60000项,主线程处理剩余项。
在C++17中,可以请求算法库做并行处理。怎么请求呢?代码如下:
#include <execution> //... int main(){ int arr[100000]; std::for_each( std::execution::par, arr, arr+10000, [](auto & x){ process(x); }); }
新的重载函数多出了个指定策略的参数std::execution::par。这个参数意味着算法库可能会偷偷的创建后台线程辅助完成for_each算法。
另外还有std::execution::seq,和std::execution::par_unseq。这些都是算法库预定义的常量。
因为标准库算法都会涉及到对容器和元素的读写操作,在多线程条件下,就会有数据竞争问题。数据竞争的解决,是库的用户责任。这意味着有时候就需要用互斥量,内存模型等机制保护数据的完整性。
例如:下面这段代码,程序可能要挂掉了。在多线程环境下,对共享的资源v的插入会出现不可预知的错误。
int a[] = {0,1,3,4,5,6,7,8,9}; std::vector<int> v; std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int i) { v.push_back(i*2+1); // 错误:数据竞争 });
最直接的解决方式,加锁保护,代码如下:
int a[] = {0,1,3,4,5,6,7,8,9}; std::vector<int> v; std::mutex m; std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int i) { std::lock_guard<std::mutex> guard(m); //互斥量保护v v.push_back(i*2+1); });
有三种并行策略,各个含义如下:
std::execution::seq
调用者线程单线程方式,以不确定的顺序访问元素
std::execution::par
多线程(由库隐式的创建线程)方式,在每个线程中,以不确定的顺序访问元素
std::execution::par_unseq
multiple threads and may be vectorized - calls are unsequenced with respect to each other and possibly interleaved
我完全没懂,需要再研究。什么是“不确定顺序的“(indeterminately sequenced)?什么是“向量化的”(vectorized)?什么是交互的“interleaved”?都是什么鬼?