2013-07-02 2 views
2

Я закодировал небольшую программу на C++, чтобы попытаться понять, как многопоточность работает с использованием std::thread. Вот шаг выполнения моей программы:Многопоточность работает неправильно, используя std :: thread (C++ 11)

  1. Инициализация матрицы 5x5 целых чисел с уникальным значением «42», содержащимся в классе «Toto» (инициализируется в основном).
  2. Я печатаю инициализированную матрицу 5x5.
  3. Заявление std::vector из 5 нитей.
  4. Я приложил все темы соответственно своей задаче (threadTask method). Каждый поток будет обрабатывать экземпляр std::vector<int>.
  5. Я присоединяюсь к всем темам.
  6. Я печатаю новое состояние моей матрицы 5x5.

Вот результат:

42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 

42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 

Оно должно быть:

42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 
42 42 42 42 42 

0 0 0 0 0 
1 1 1 1 1 
2 2 2 2 2 
3 3 3 3 3 
4 4 4 4 4 

Вот пример кода:

#include <iostream> 
#include <vector> 
#include <thread> 

class  Toto 
{ 
public: 
    /* 
    ** Initialize a 5x5 matrix with the 42 value. 
    */ 
    void initData(void) 
    { 
     for (int y = 0; y < 5; y++) { 
      std::vector<int> vec; 

      for (int x = 0; x < 5; x++) { 
       vec.push_back(42); 
      } 
      this->m_data.push_back(vec); 
     } 
    } 

    /* 
    ** Display the whole matrix. 
    */ 
    void printData(void) const 
    { 
     for (int y = 0; y < 5; y++) { 
      for (int x = 0; x < 5; x++) { 
       printf("%d ", this->m_data[y][x]); 
      } 
      printf("\n"); 
     } 
     printf("\n"); 
    } 

    /* 
    ** Function attached to the thread (thread task). 
    ** Replace the original '42' value by the another one. 
    */ 
    void threadTask(std::vector<int> &list, int value) 
    { 
     for (int x = 0; x < 5; x++) { 
      list[x] = value; 
     } 
    } 

    /* 
    ** Return a sub vector reference according to the range. 
    */ 

    std::vector<int> &getDataByRange(int range) 
    { 
     return (this->m_data[range]); 
    } 

    private: 
     std::vector<std::vector<int> > m_data; 
}; 

int   main(void) 
{ 
    Toto toto; 

    toto.initData(); 

    toto.printData(); //Display the original 5x5 matrix (first display). 

    std::vector<std::thread> threadList(5); //Initialization of vector of 5 threads. 

    for (int i = 0; i < 5; i++) { //Threads initializationss 

     std::vector<int> &vec = toto.getDataByRange(i); //Get each sub-vectors reference. 
     threadList.at(i) = std::thread(&Toto::threadTask, toto, vec, i); //Each thread will be attached to a specific vector. 
    } 

    for (int j = 0; j < 5; j++) { 
     threadList.at(j).join(); 
    } 

    toto.printData(); //Second display. 
    getchar(); 

    return (0); 
} 

Однако в методе threadTask, если я печатаю переменная list[x], выход c дый правильный. Я думаю, что я не могу напечатать правильные данные в основном, потому что вызов printData() находится в основном потоке, и отображение в функции threadTask правильное, потому что метод выполняется в своем потоке (а не в основном). Странно, это означает, что все потоки, созданные в родительских процессах, не могут изменить данные в этих родительских процессах? Я думаю, что что-то забыл в своем коде. Я действительно потерян. Кто-нибудь может мне помочь, пожалуйста? Большое спасибо за вашу помощь.

+8

Вам нужно узнать о значении C++ и эталонной семантике. 'std :: vector vec = toto.getData() [i];' создает копию вектора, поэтому оригинал никогда не изменяется. Затем эта копия также копируется в объект потока, и снова никогда не влияет на оригинал. –

+0

Я обновил свой вопрос, используя новую функцию getDataByRange, которая возвращает ссылку на определенный суб-вектор в соответствии с диапазоном.Поэтому я не верну копию, но ссылку. Результат такой же, как после моей модификации. – user1364743

+0

Проблема не в функции getData, это было с std :: vector vec = ... part. Это вызывает конструктор экземпляров vecs, а vec будет другим объектом, чем тот, который возвращается getData. В качестве примечания стороны. Рассмотрим инициализацию вашего вектора в конструкторе классов с помощью конструкторов векторов. Toto(): m_data (5, std :: vector (5, 42)) { } является, по-моему, намного более чистой реализацией. –

ответ

2

Я решил свою проблему. Все идет от линии:

threadList.at(i) = std::thread(&Toto::threadTask, toto, **vec**, i); 

, которые должны быть:

threadList.at(i) = std::thread(&Toto::threadTask, toto, **&vec**, i); 

и моя функция «» threadTask должны принимает указатель (а не ссылку) в первом параметре.

void threadTask(std::vector<int> *list, int value) 
    { 
     printf("b=%p\n", list); 
     getchar(); 
     for (int x = 0; x < 5; x++) { 
      (*list)[x] = value; 
     } 
    } 

Надеюсь, этот пример поможет кому-то.

+4

В качестве альтернативы вы можете использовать ссылку, например, первую попытку. Однако вы бы назвали std :: thread с std :: ref как параметр: 'std :: thread (& Toto :: threadTask, toto, std :: ref (vec), i);' – James

3

Мне кажется, что многие из вариантов, которые вы сделали в письменном виде, делают вашу работу существенно сложнее, чем необходимо. В частности, ваш класс toto кажется мне для того, чтобы сделать клиентский код более сложным, а не простым, и не может инкапсулировать данные, которыми он управляет. Я думаю, что если бы я собирался это сделать, я бы написал еще код по этим общим строкам:

#include <iostream> 
#include <vector> 
#include <thread> 
#include <algorithm> 

std::ostream &operator<<(std::ostream &os, std::vector<int> const &d) { 
    for (auto const &v : d) 
     os << v << "\t"; 
    return os; 
} 

std::ostream &operator<<(std::ostream &os, std::vector <std::vector<int>> const &d) { 
    for (auto const &v : d) 
     os << v << "\n"; 
    return os; 
} 

int main(void) { 
    std::vector<std::vector<int>> d(5, std::vector<int>(5, 42)); 

    std::cout << d; 

    std::vector<std::thread> threads; 

    for (int i = 0; i < 5; i++) 
     threads.emplace_back([i, &d]() {std::fill(d[i].begin(), d[i].end(), i); }); 

    std::for_each(threads.begin(), threads.end(), [](std::thread &t){t.join(); }); 

    std::cout << "\n" << d; 
} 
Смежные вопросы