考慮一個算法,代碼如下:
#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”?都是什么鬼?