如何将此代码重构为多线程版本?

时间:2017-09-19 13:17:02

标签: c++ multithreading c++11

有一个循环需要很长时间,我正在考虑将此代码重构为多线程版本。这是模型。

   Photon photon;
    for (int i=0;i<1000000;++i){
         func(){
          photon.lanuch(args...){
          // do something  
          }
      }
    }

我必须将这个函数调用一千零一千次。所以我想知道如何在某个时候创建​​一些线程来运行这个函数。 但光子必须每次都是个体。 我可以将索引转换为:

 atomic<int> i{0};
    while(i<1000000){
          func(){
              photon.lanuch(args...){
              // do something  
             ++i;
               }
          }
    }

2 个答案:

答案 0 :(得分:2)

非常多的取决于photon.launch()如何以及在多大程度上可以并行化。

下面的代码将范围划分为(大约)相等的段,然后在单独的线程中执行每个段。

如上所述,这有助于取决于photon.launch()可以并行完成多少。如果它花费大部分时间修改共享状态,并且基本上具有以下形式:

    void launch(int index){
        std::lock_guard<std::mutex> guard{m};
        //.....
    }

如果mPhoton的成员,则很少会获得任何内容。 如果(在另一个极端),对launch的个别呼叫永远不会争用相同的数据,那么它可以并行化到系统可以提供的核心数量。

#include <thread>
#include <vector>

class Photon {
    public: 
    void launch(int index){
        //... what goes here matters a lot...
    }
};


void photon_launch(Photon& photon,int from,int to){
    for(auto i=from;i<=to;++i){
        photon.launch(i);
    }
}


int main() {
    const size_t loop_count=100000;//How big is the loop?
    const size_t thread_count=4;//How many threads can we utilize?
    std::vector< std::thread > threads;

    Photon photon;
    int from=1;
    for(size_t i=1;i<=thread_count;++i){

        //If loop_count isn't divisible by thread_count evens out the remainder.        
        int to=(loop_count*i)/thread_count;
        threads.emplace_back(photon_launch,std::ref(photon),from,to);
        from=to+1;
    }

    //Now the threads are launched we block until they all finish.
    //If we don't the program may (will?) finish before the threads.
    for(auto& curr : threads){
        curr.join();
    }

    return 0;
}

答案 1 :(得分:2)

使用线程,你必须注意对象的生命周期和分享远远超过正常。

但基本的解决方案是

void do_tasks( std::size_t count, std::function<void( std::size_t start, std::size_t finish )> task ) {
  auto thread_count = std::thread::hardware_concurrency();
  if (thread_count <= 0) thread_count = 1;

  std::vector<std::future<void>> threads( thread_count-1 );

  auto get_task = [=](std::size_t index) {
    auto start = count * index / thread_count;
    auto finish = count * (index+1) / thread_count;
    // std::cout << "from " << start << " to " << finish << "\n";
    return [task, start, finish]{ task(start, finish); };
  };
  for( auto& thread : threads ) {
    auto index = &thread-threads.data();
    thread = std::async( std::launch::async, get_task(index) );
  }
  get_task( threads.size() )();
  for (auto& thread : threads) {
    thread.get();
  }
}

这是一个小型多线程库。

你这样使用它:

do_tasks( 100, [&](size_t start, size_t finish) {
  // do subtasks starting at index start, up to and not including finish
});

还有其他更复杂的线程库,但是写一个小的一半也不难,所以我做了。

明确:

Photon photon;
do_tasks( 1000000, [&](size_t start, size_t finish) {
  for (int i = start; i < finish; ++i) {
    photon.lanuch(args...){
  }
});

但是你必须非常小心,确保线程之间没有不安全的数据共享,并且你不只是阻塞常见互斥锁上的每个线程。

Live example