C++17对不少现有的function提供parallel版本,例如:
std::copy, std::find, std::sort, std::unique...
(但有些function没有,例如:std::sort_heap, std::copy_backward...)
要怎么使用这些parallel function,就得了解本文要介绍的execution policy
execution policy目前分成3种class,分别是
std::sequential_execution_policy,
std::parallel_execution_policy,
std::parallel_vector_execution_policy
而在namespace std里,已经有这3个class的object,分别是
std::sequential, std::par, std::par_vec
(关系如同std::defer_lock_t与std::defer_lock)
因此使用上,直接使用这3个object就可以
接下来将介绍不用execution policy的for_each,以及使用execution policy的for_each
(底下的std::就不打了)
vector<int> vec{1,2};
const auto func{[](const auto val){
cout<<val;
cout<<val;
}};
不用execution policy:
for_each(cbegin(vec),cend(vec),func);
执行结果:
1122
用sequential_execution_policy:
for_each(sequential,cbegin(vec),cend(vec),func);
执行结果可能是:
1122
2211
用sequential_execution_policy,表示func不一定会按照顺序执行
但同一时间,绝对不会有2个func被执行(所以不用避免data race)
用parallel_execution_policy:
for_each(par,cbegin(vec),cend(vec),func);
执行结果可能是:
1122(由1个或2个thread执行的结果)
1212(由2个thread执行的结果)
1221(由2个thread执行的结果)
2112(由2个thread执行的结果)
2121(由2个thread执行的结果)
2211(由1个或2个thread执行的结果)
用parallel_execution_policy时,要避免data race的问题
到这边为止,以上两个都很正常,然而parallel_vector_execution_policy就比较特别了
用parallel_vector_execution_policy:
for_each(par_vec,cbegin(vec),cend(vec),func);
执行结果跟parallel_execution_policy一样,但是多增加4种可能的结果
对于1212, 1221, 2112, 2121,他们有可能是由1个thread执行的结果
也就是说,当这个thread执行完func的第一个cout<<val;时
他可能会跳去执行别的func的code(!?)
这边就要说一下code执行的顺序
一般来说,在同个thread底下,给予2个evaluation,A与B
要不就是A先执行,之后换B;不然就是B先执行,之后换A
然而,使用parallel_vector_execution_policy时,就会产生另一种情况
就是A先执行,然后A还没执行完,就跳去执行B(这情况叫做unsequenced)
也就是说,1212的执行结果由来可能是
main thread(呼叫for_each的thread)执行第一个func
main thread执行cout<<val;
main thread跳到第二个func
main thread执行cout<<val;(第二个func的第一个cout<<val;)
main thread跳回第一个func
main thread执行cout<<val;(第一个func的第二个cout<<val;)
main thread跳到第二个func
main thread执行cout<<val;(第二个func的第二个cout<<val;)
因此在用parallel_vector_execution_policy时,要非常小心,举例来说:
mutex mut;
for_each(par_vec,cbegin(vec),cend(vec),[&](const auto val){
lock_guard<mutex> lock{mut};
cout<<val;
});
当第一个func执行完lock_guard<mutex> lock{mut};后
他可能会跳到另一个func,然后又执行lock_guard<mutex> lock{mut};
此时就会发生undefined behavior
结语:
虽然以上讲了那么多,但是目前还没有compiler支援execution policy
所以各位可能还要等一段时间,才能使用这些特性了
注:
有些补充摆到另一篇文章了(#1NEeDZ3X)