@@ -77,9 +77,9 @@ int main(){
77
77
我们可以举个简单的例子运用这个值:
78
78
79
79
``` cpp
80
- template <typename InputIt >
81
- auto sum (InputIt first, InputIt last){
82
- using value_type = std::iter_value_t<InputIt >;
80
+ template <typename ForwardIt >
81
+ auto sum (ForwardIt first, ForwardIt last){
82
+ using value_type = std::iter_value_t<ForwardIt >;
83
83
std::size_t num_threads = std::thread : :hardware_concurrency();
84
84
std::ptrdiff_t distance = std::distance(first, last);
85
85
@@ -95,9 +95,9 @@ auto sum(InputIt first, InputIt last){
95
95
std::vector<std::thread> threads;
96
96
97
97
// 创建并启动线程
98
- InputIt start = first;
98
+ auto start = first;
99
99
for (std::size_t i = 0; i < num_threads; ++i) {
100
- InputIt end = std::next(start, chunk_size + (i < remainder ? 1 : 0));
100
+ auto end = std::next(start, chunk_size + (i < remainder ? 1 : 0));
101
101
threads.emplace_back([start, end, &results, i] {
102
102
results[i] = std::accumulate(start, end, value_type{});
103
103
});
@@ -118,18 +118,18 @@ auto sum(InputIt first, InputIt last){
118
118
}
119
119
```
120
120
121
- > [运行](https://godbolt.org/z/7d9Yex8vn )测试。
121
+ > [运行](https://godbolt.org/z/8oq3MnvT5 )测试。
122
122
123
123
我们写了这样一个求和函数 `sum`,接受两个迭代器计算它们范围中对象的和。
124
124
125
125
我们先获取了迭代器所指向的值的类型,定义了一个别名 `value_type`,我们这里使用到的 [`std::iter_value_t`](https://zh.cppreference.com/w/cpp/iterator/iter_t) 是 C++20 引入的,[返回类型推导](https://zh.cppreference.com/w/cpp/language/function#.E8.BF.94.E5.9B.9E.E7.B1.BB.E5.9E.8B.E6.8E.A8.E5.AF.BC)是 C++14 引入。如果希望代码可以在 C++11 的环境运行也可以自行修改为:
126
126
127
127
```cpp
128
- template<typename InputIt,typename value_type = typename std::remove_cv<typename std::remove_reference<decltype(*std::declval<InputIt>())>::type>::type >
129
- value_type sum(InputIt first, InputIt last);
128
+ template<typename ForwardIt >
129
+ typename std::iterator_traits<ForwardIt>:: value_type sum(ForwardIt first, ForwardIt last);
130
130
```
131
131
132
- > [ 运行] ( https://godbolt.org/z/fTcYeKE67 ) 测试。
132
+ > [ 运行] ( https://godbolt.org/z/4E17nTs4d ) 测试。
133
133
134
134
` num_threads ` 是当前硬件支持的并发线程的值。[ ` std::distance ` ] ( https://zh.cppreference.com/w/cpp/iterator/distance ) 用来计算 first 到 last 的距离,也就是我们要进行求和的元素个数了。
135
135
@@ -139,7 +139,7 @@ value_type sum(InputIt first, InputIt last);
139
139
140
140
1 . ` chunk_size ` 是每个线程分配的任务,但是这是可能有余数的,比如 10 个任务分配三个线程,必然余 1。但是我们也需要执行这个任务,所以还定义了一个对象 ` remainder ` ,它存储的就是余数。
141
141
142
- 2 . ` InputIt end = std::next(start, chunk_size + (i < remainder ? 1 : 0));` 这行代码是获取当前线程的执行范围,其实也就是要 ` chunk_size ` 再加上我们的余数 ` remainder ` 。这里写了一个三目运算符是为了进行分配任务,比如:
142
+ 2 . ` auto end = std::next(start, chunk_size + (i < remainder ? 1 : 0));` 这行代码是获取当前线程的执行范围,其实也就是要 ` chunk_size ` 再加上我们的余数 ` remainder ` 。这里写了一个三目运算符是为了进行分配任务,比如:
143
143
144
144
假设有 3 个线程执行,并且余数是 2。那么,每个线程的处理情况如下:
145
145
@@ -149,7 +149,7 @@ value_type sum(InputIt first, InputIt last);
149
149
150
150
这确保了** 剩余** 的 2 个元素被分配给了前两个线程,而第三个线程只处理了平均数量的元素。这样就确保了所有的元素都被正确地分配给了各个线程进行处理。
151
151
152
- 3 . ` InputIt start = first;` 在创建线程执行之前先定义了一个开始迭代器。在传递给线程执行的lambda表达式中,最后一行是:` start = end; ` 这是为了让迭代器一直向前。
152
+ 3 . ` auto start = first;` 在创建线程执行之前先定义了一个开始迭代器。在传递给线程执行的lambda表达式中,最后一行是:` start = end; ` 这是为了让迭代器一直向前。
153
153
154
154
由于求和不涉及数据竞争之类的问题,所以我们甚至可以在刚讲完 ` Hello World ` 就手搓了一个"** 并行求和** "的简单的模板函数。主要的难度其实在于对 C++ 的熟悉程度,而非对线程类 ` std::thread ` 的使用了,这里反而是最简单的,无非是用容器存储线程对象管理,最后进行 ` join() ` 罢了。
155
155
0 commit comments