В чем разница между передачей счетчика цикла или объявления диапазона потоку по ссылке?

0
#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-loop на основе диапазона корректно печатаются цифры 0-99 (хотя и не обязательно в порядке, а иногда с переводом строк в очереди неправильно), но цикл integer вызывает распечатку следующим образом:

1
2
3
4
5
6
7
8
9
13
1414

14
14
15
16
18
18
19
...
99
100

Как я понимаю, целое передается по ссылке на поток, но его значение изменяется в основном цикле до того, как поток получает его распечатать, в результате чего некоторые значения не распечатываются, некоторые значения будут распечатываться больше чем один раз, и значение 100 для печати, хотя i получаю это значение после создания последнего потока.

Однако я не понимаю, почему это также не проблема при использовании цикла for-loop.

Почему версия на основе циклов для цикла не страдает от тех же проблем параллелизма, что и цикл простого целого в этом коде?

Теги:
multithreading
c++11
boost-thread

1 ответ

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, поэтому они не меняются в цикле.

Он эффективно раскрывается:

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++

  • 0
    Но я беру ссылку на эту ссылку, и эта оригинальная ссылка меняется, чтобы ссылаться на разные дискретные случаи. Почему это не проблема?
  • 0
    Потому что это разные ссылочные экземпляры. Это эффективно разворачивается к этому: for (auto itr = begin(integers); itr != end(integers); ++itr) { itr const& i = *itr; // pass reference to threads } Вы не можете переустановить ссылку в C ++

Ещё вопросы

Сообщество Overcoder
Наверх
Меню