Распараллелить цикл, используя std :: thread и передовой опыт

Возможный дубликат:
C ++ 2011: std :: thread: простой пример распараллеливания цикла?

Рассмотрим следующую программу, которая распределяет вычисления по элементам вектора (я никогда раньше не использовал std :: thread):

// vectorop.cpp
// compilation: g++ -O3 -std=c++0x vectorop.cpp -o vectorop -lpthread
// execution: time ./vectorop 100 50000000
// (100: number of threads, 50000000: vector size)
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <vector>
#include <thread>
#include <cmath>
#include <algorithm>
#include <numeric>

// Some calculation that takes some time
template<typename T>
void f(std::vector<T>& v, unsigned int first, unsigned int last) {
for (unsigned int i = first; i < last; ++i) {
v[i] = std::sin(v[i])+std::exp(std::cos(v[i]))/std::exp(std::sin(v[i]));
}
}

// Main
int main(int argc, char* argv[]) {

// Variables
const int nthreads = (argc > 1) ? std::atol(argv[1]) : (1);
const int n = (argc > 2) ? std::atol(argv[2]) : (100000000);
double x = 0;
std::vector<std::thread> t;
std::vector<double> v(n);

// Initialization
std::iota(v.begin(), v.end(), 0);

// Start threads
for (unsigned int i = 0; i < n; i += std::max(1, n/nthreads)) {
// question 1:
// how to compute the first/last indexes attributed to each thread
// with a more "elegant" formula ?
std::cout<<i<<" "<<std::min(i+std::max(1, n/nthreads), v.size())<<std::endl;
t.push_back(std::thread(f<double>, std::ref(v), i, std::min(i+std::max(1, n/nthreads), v.size())));
}

// Finish threads
for (unsigned int i = 0; i < t.size(); ++i) {
t[i].join();
}
// question 2:
// how to be sure that all threads are finished here ?
// how to "wait" for the end of all threads ?

// Finalization
for (unsigned int i = 0; i < n; ++i) {
x += v[i];
}
std::cout<<std::setprecision(15)<<x<<std::endl;
return 0;
}

В коде уже есть два вопроса.

Третьим было бы: этот код полностью в порядке или он может быть написан более элегантно с использованием std :: threads? Я не знаю «хороших практик», использующих std :: thread …

1

Решение

На первый вопрос, как вычислить диапазоны для вычисления для каждого потока: я извлек константы и дал им имена, чтобы облегчить чтение кода.
Для хорошей практики я также использовал лямбда что делает код более легким для изменения — код в лямбде будет использоваться только здесь, в то время как функция f может быть использован из другого кода по всей программе. Используйте это, чтобы поместить общие части кода в функцию и специализированную функцию, которые используются только один раз в лямбда-выражении.

const size_t itemsPerThread = std::max(1, n/threads);
for (size_t nextIndex= 0; nextIndex< v.size(); nextIndex+= itemsPerThread)
{
const size_t beginIndex = nextIndex;
const size_t endIndex =std::min(nextIndex+itemsPerThread, v.size())
std::cout << beginIndex << " " << endIndex << std::endl;
t.push_back(std::thread([&v,beginIndex ,endItem]{f(v,beginIndex,endIndex);});
}

В расширенном случае использования будет использоваться пул потоков, но как это будет выглядеть, зависит от дизайна вашего приложения и не покрывается STL. Для хорошего примера модели потоков см. Qt Framework. Если вы только начинаете с потоками, сохраните это на потом.

На второй вопрос уже ответили в комментариях. std::thread::join Функция будет ждать (блокировать), пока поток не закончится. Вызвав функцию join в каждом потоке и получив код после функции join, вы можете быть уверены, что все существующие потоки завершились и теперь могут быть удалены.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]