2015-02-09 1 views
1

Я пишу программу Монте-Карло в C++ и использую std :: thread, чтобы разделить количество историй, которые нужно отслеживать между потоками. Тем не менее, это моя первая попытка многопоточности, и я столкнулся с проблемой, которую этот упрощенный код, надеюсь, позволит мне продемонстрировать в надежде, что я получу некоторые советы от читателей этого сайта. В этой упрощенной задаче я вызываю функцию Summation, которая производит 1X5 мерный массив случайных чисел с 2 потоками. Когда потоки возвращают свои значения (на самом деле не возвращаются, поскольку это глобальная переменная), основная программа затем имеет два массива с пятью размерами, каждый из которых соответствует другому потоку. Я хотел бы объединить два массива в один массив с элементами в конечном массиве, соответствующие сумме тех же элементов в двух массивах, которые были созданы разными потоками. К сожалению, массивы из каждого потока имеют одно и то же имя, поэтому я не могу просто добавить два разных массива. Какие методы рекомендуется объединить два массива размером 1X5 в единый массив суммирования с элементами, соответствующими сумме идентичных элементов из каждого потока?Объединение массивов из нескольких потоков с использованием std :: thread

#include <iostream> 
#include <vector> 
#include <thread> 
#include <mutex> 
#include <cassert> 
#include "boost/multi_array.hpp" 

std::vector<float> Array; 
std::mutex Array_mutex; 

void Summation(int sample_size) 
{ 
    std::lock_guard<std::mutex> guard(Array_mutex); 
    for(int i = 0; i < sample_size; i++) 
    { 
     Array.push_back(rand() % 10 + 1); 
    } 
    std::cout << "\n"; 
} 

int main(int argc, const char * argv[]) { 
    int sample_size = 10; 
    int Num_Threads = 2; 
    int number_count = sample_size/Num_Threads; 
    srand(time(NULL)); 
    std::vector<std::thread> Threads; 
    for(int i = 0; i < Num_Threads; i++) 
    { 
     Threads.push_back(std::thread(Summation,number_count)); 
    } 

    for(int i = 0; i < Num_Threads; i++) 
    { 
     Threads[i].join(); 
    } 

    // - I would like to combine the arrays produced from each thread into a 
    // single array, where each element in the final array is the sum of 
    // the identical element in the array from each thread 

    // i.e. Element 1(final) = Element 1(thread 1) + Element 1(thread2) 
    //  Element 2(final) = Element 2(thread 1) + Element 2(thread2) 
    //  Element 3(final) = Element 3(thread 1) + Element 3(thread2) 

    return 0; 
} 
+1

Две вещи: пусть каждый протектор выполняет немного вычислений, поэтому один поток выполняет записи с 1 по 10, еще 11-20 и т. Д. Если вектор создается с фиксированным размером (или если вы используете 'std :: array'), вам не нужна блокировка. Другое дело, инициализировать генератор случайных чисел * только один раз. –

+0

Кроме того, вы не * имеете * несколько массивов (или векторов) для объединения в свой код, все потоки будут записываться в один и тот же массив. Фактически, пренебрегайте первой частью моего предыдущего комментария, так как ваша проблема - еще одна, и это значит, что у вас нет нескольких векторов, только один. –

+0

O.K., это хорошая информация, которая меняет мое понимание вывода. Если это тот же массив, то это означает, что данные из одного потока перезаписывают данные, созданные вторым потоком, поэтому я получаю два массива, когда я печатаю их командой std :: cout, но на самом деле это один и тот же массив/слот памяти, напечатанный дважды с перезаписанными данными. Если это так, то как я могу получить каждый поток для создания отдельного массива, который может быть объединен в другой поток или главную программу? – Jon

ответ

1

Если вы хотите один вектор для каждого потока, вы на самом деле нужно иметь один вектор для каждого потока. Как вектор векторов.

Для простого и наивного решения, что-то вроде

#include <iostream> 
#include <array> 
#include <random> 
#include <thread> 

void generate(const size_t size, std::array<float>& values) 
{ 
    // Pseudo-random number generation stuff 
    std::random_device rd; 
    std::default_random_engine e1(rd()); 
    std::uniform_int_distribution<float> uniform_dist(1, 10); 

    // Generate some values and add the array 
    for (size_t i = 0; i < size; ++i) 
     values[i] = uniform_dist(el); 
} 

int main() 
{ 
    constexpr size_t number_values = 10; 
    constexpr size_t number_threads = 2; 

    // An array of arrays, one sub-array per thread 
    std::array<std::array<float, number_values>, number_threads> 
     values; 

    // An array of threads 
    std::array<std::thread, number_threads> threads; 

    // Create threads 
    for (size_t i = 0; i < number_threads; ++i) 
     threads[i] = std::thread(generate, number_values, std::ref(values[i])); 

    // Wait for threads to finish 
    for (size_t i = 0; i < number_threads; ++i) 
     threads[i].join(); 

    // Now "combine" the values into a single array 
    std::array<float, number_values> totals; 
    for (size_t i = 0; i < number_values; ++i) 
    { 
     for (size_t j = 0; j < number_threads; ++j) 
      totals[i] += values[j][i]; 
    } 

    // Print the values 
    for (const size_t i; i < number_values; ++i) 
     std::cout << "Value #" << (i + 1) << " = " << totals[i] << '\n'; 
} 

Обратите внимание, что код тестировался, и даже не компилируется, но должен работать в теории. :)

+0

Благодарим вас за пример, картина рисует тысячу слов! ;-) – Jon

+0

За исключением нескольких проблем, код работает очень хорошо. Единственная проблема заключалась в том, что массив значений должен выделяться в вызове функции. К сожалению, моя проблема требует распределения динамических массивов, так как я никогда не знаю, сколько мониторов monte carlo будут использоваться в определенных пользователем проблемах. Я пробовал все, что мог придумать с помощью вектора и очков, и ничего не получилось. В конце я дал справедливый путь по выделенному массиву, чтобы пользователь мог определить столько детекторов, сколько пожелает. Спасибо за помощь в этом примере. – Jon