В чем разница между передачей счетчика цикла или объявления диапазона потоку по ссылке?
#include <iostream>
#include <memory>
#include <vector>
#include "boost/thread.hpp"
using boost::thread;
using std::vector;
using std::unique_ptr;
class ThreadPool {
public:
ThreadPool(int size) : max_size_(size) {}
~ThreadPool() {
Join();
}
void PollForSpace() {
while (static_cast<int>(pool_.size()) >= max_size_) {
for (auto it = pool_.begin(); it != pool_.end(); ++it) {
if ((*it)->timed_join(boost::posix_time::milliseconds(10))) {
pool_.erase(it);
break;
}
}
}
}
void AddToPool(unique_ptr<thread> t) {
pool_.push_back(std::move(t));
}
void Join() {
for (auto it = pool_.begin(); it != pool_.end(); ++it) {
(*it)->join();
}
pool_.clear();
}
protected:
vector<unique_ptr<thread> > pool_;
int max_size_;
};
int main(int argc, char** argv) {
ThreadPool pool(20);
std::vector<int> integers;
for (int i = 0; i < 100; ++i) {
integers.push_back(i);
}
std::cout << "Range based for loop over vector." << std::endl;
for (auto const& i : integers) {
pool.PollForSpace();
pool.AddToPool(unique_ptr<thread>(new thread([&i]{
std::cout << i << std::endl;
})));
}
pool.Join();
std::cout << "Integer loop." << std::endl;
for (int i = 0; i < 100; ++i) {
pool.PollForSpace();
pool.AddToPool(unique_ptr<thread>(new thread([&i]{
std::cout << i << std::endl;
})));
}
pool.Join();
return 0;
}
Почему цикл for, основанный на диапазоне, правильно печатает числа 0-99 (хотя и не обязательно по порядку, а иногда с ошибками перемежаются строки), но целочисленный цикл вызывает печать следующим образом:
1
2
3
4
5
6
7
8
9
13
1414
14
14
15
16
18
18
19
...
99
100
Насколько я понимаю, целое число передается по ссылке на поток, но его значение изменяется в главном цикле, прежде чем поток может его распечатать, что приводит к тому, что некоторые значения не распечатываются, некоторые значения распечатываются больше. чем один раз, и значение 100 будет напечатано, даже если i
получает это значение после создания последнего потока.
Однако я не понимаю, почему это также не проблема при использовании цикла for на основе диапазона.
Почему основанная на диапазоне версия for-loop не страдает от тех же проблем параллелизма, что и простой целочисленный цикл в этом коде?
1 ответ
for (int i = 0; i < 100; ++i) {
pool.PollForSpace();
pool.AddToPool(unique_ptr<thread>(new thread([&i]{
std::cout << i << std::endl;
})));
}
Вы захватываете переменную i в лямбду по ссылке. Все потоки теперь имеют ссылку на один и тот же экземпляр i, который вы меняете в цикле. Вы уже упоминали, что знаете об этом
Ваш подход, основанный на диапазоне, имеет вектор с дискретными экземплярами i, поэтому они не меняются в цикле.
Это эффективно разворачивается к этому:
for (auto itr = begin(integers); itr != end(integers); ++itr)
{
int const& i = *itr;
// pass new reference to each thread
pool.PollForSpace();
pool.AddToPool(unique_ptr<thread>(new thread([&i]{
std::cout << i << std::endl;
})));
}
Вы не можете переустановить ссылку в C++