Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 02ccd60

Browse files
frederick-vs-jaMq-b
authored andcommitted
并行算法使用向前迭代器
C++11 中迭代器的值类型应使用 `std::iterator_traits<I>::value_type` 确定。
1 parent 72c6b94 commit 02ccd60

File tree

2 files changed

+17
-17
lines changed

2 files changed

+17
-17
lines changed

‎md/02使用线程.md‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ int main(){
7777
我们可以举个简单的例子运用这个值:
7878

7979
```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>;
8383
std::size_t num_threads = std::thread::hardware_concurrency();
8484
std::ptrdiff_t distance = std::distance(first, last);
8585

@@ -95,9 +95,9 @@ auto sum(InputIt first, InputIt last){
9595
std::vector<std::thread> threads;
9696

9797
// 创建并启动线程
98-
InputIt start = first;
98+
auto start = first;
9999
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));
101101
threads.emplace_back([start, end, &results, i] {
102102
results[i] = std::accumulate(start, end, value_type{});
103103
});
@@ -118,18 +118,18 @@ auto sum(InputIt first, InputIt last){
118118
}
119119
```
120120
121-
> [运行](https://godbolt.org/z/7d9Yex8vn)测试。
121+
> [运行](https://godbolt.org/z/8oq3MnvT5)测试。
122122
123123
我们写了这样一个求和函数 `sum`,接受两个迭代器计算它们范围中对象的和。
124124
125125
我们先获取了迭代器所指向的值的类型,定义了一个别名 `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 的环境运行也可以自行修改为:
126126
127127
```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);
130130
```
131131

132-
> [运行](https://godbolt.org/z/fTcYeKE67)测试。
132+
> [运行](https://godbolt.org/z/4E17nTs4d)测试。
133133
134134
`num_threads` 是当前硬件支持的并发线程的值。[`std::distance`](https://zh.cppreference.com/w/cpp/iterator/distance) 用来计算 first 到 last 的距离,也就是我们要进行求和的元素个数了。
135135

@@ -139,7 +139,7 @@ value_type sum(InputIt first, InputIt last);
139139

140140
1. `chunk_size` 是每个线程分配的任务,但是这是可能有余数的,比如 10 个任务分配三个线程,必然余 1。但是我们也需要执行这个任务,所以还定义了一个对象 `remainder` ,它存储的就是余数。
141141

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` 。这里写了一个三目运算符是为了进行分配任务,比如:
143143

144144
假设有 3 个线程执行,并且余数是 2。那么,每个线程的处理情况如下:
145145

@@ -149,7 +149,7 @@ value_type sum(InputIt first, InputIt last);
149149

150150
这确保了**剩余**的 2 个元素被分配给了前两个线程,而第三个线程只处理了平均数量的元素。这样就确保了所有的元素都被正确地分配给了各个线程进行处理。
151151

152-
3. `InputIt start = first;` 在创建线程执行之前先定义了一个开始迭代器。在传递给线程执行的lambda表达式中,最后一行是:`start = end;` 这是为了让迭代器一直向前。
152+
3. `auto start = first;` 在创建线程执行之前先定义了一个开始迭代器。在传递给线程执行的lambda表达式中,最后一行是:`start = end;` 这是为了让迭代器一直向前。
153153

154154
由于求和不涉及数据竞争之类的问题,所以我们甚至可以在刚讲完 `Hello World` 就手搓了一个"**并行求和**"的简单的模板函数。主要的难度其实在于对 C++ 的熟悉程度,而非对线程类 `std::thread` 的使用了,这里反而是最简单的,无非是用容器存储线程对象管理,最后进行 `join()` 罢了。
155155

‎md/04同步操作.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -499,9 +499,9 @@ int main() {
499499
我们再将第二章实现的并行 `sum` 改成 `std::package_task` + `std::future` 的形式:
500500

501501
```cpp
502-
template<typename InputIt>
503-
auto sum(InputIt first, InputIt last) {
504-
using value_type = std::iter_value_t<InputIt>;
502+
template<typename ForwardIt>
503+
auto sum(ForwardIt first, ForwardIt last) {
504+
using value_type = std::iter_value_t<ForwardIt>;
505505
std::size_t num_threads = std::thread::hardware_concurrency();
506506
std::ptrdiff_t distance = std::distance(first, last);
507507

@@ -519,9 +519,9 @@ auto sum(InputIt first, InputIt last) {
519519
std::vector<std::thread> threads;
520520

521521
// 制作任务、与 future 关联、启动线程执行
522-
InputIt start = first;
522+
auto start = first;
523523
for (std::size_t i = 0; i < num_threads; ++i) {
524-
InputIt end = std::next(start, chunk_size + (i < remainder ? 1 : 0));
524+
auto end = std::next(start, chunk_size + (i < remainder ? 1 : 0));
525525
tasks.emplace_back(std::packaged_task<value_type()>{[start, end, i] {
526526
return std::accumulate(start, end, value_type{});
527527
}});
@@ -547,7 +547,7 @@ auto sum(InputIt first, InputIt last) {
547547
}
548548
```
549549
550-
> [运行](https://godbolt.org/z/z4PMYaznG)测试。
550+
> [运行](https://godbolt.org/z/r19MYcv6e)测试。
551551
552552
相比于之前,其实不同无非是定义了 `std::vector<std::packaged_task<value_type()>> tasks` 与 `std::vector<std::future<value_type>> futures` ,然后在循环中制造任务插入容器,关联 tuple,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。
553553

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /