通过引用将循环计数器或范围声明传递给线程有什么区别


#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 循环版本没有与此代码中的纯整数循环相同的并发问题?

for (int i = 0; i < 100; ++i) {
    pool.PollForSpace();
    pool.AddToPool(unique_ptr<thread>(new thread([&i]{
        std::cout << i << std::endl;
      })));
}

您正在通过引用将变量 i 捕获到 lambda。现在,所有线程都具有对同一 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++ 中的引用

最新更新