C++: передача threadID для функции аномалии

Я реализовал параллельную очередь с двумя методами: добавить (поставить в очередь) и удалить (удалить из очереди).

Чтобы протестировать свою реализацию с использованием двух потоков, я сгенерировал 10 (NUMBER_OF_OPERATIONS) случайных чисел от 0 до 1 в методе getRandom(). Это позволяет мне создавать различные распределения операций добавления и удаления.

Метод doWork разделяет проделанную работу по количеству потоков.

ПРОБЛЕМА: идентификатор потока, который я передаю из основной функции, не соответствует идентификатору потока, который получает метод doWork. Вот несколько примеров прогонов:

Вывод 1

Вывод 2

 #define NUMBER_OF_THREADS 2
 #define NUMBER_OF_OPERATIONS 10

 int main () {

        BoundedQueue<int> bQ;
        std::vector<double> temp = getRandom();
        double* randomNumbers = &temp[0];
        std::thread myThreads[NUMBER_OF_THREADS];

        for(int i = 0; i < NUMBER_OF_THREADS; i++) {
            cout << "Thread " << i << " created.\n";
            myThreads[i] = std::thread ( [&] { bQ.doWork(randomNumbers, i); });
        }

        cout << "Main Thread\n";

        for(int i = 0; i < NUMBER_OF_THREADS; i++) {
            if(myThreads[i].joinable()) myThreads[i].join();
        }   
        return 0;
    }

    template <class T> void BoundedQueue<T>::doWork (double randomNumbers[], int threadID) {

        cout << "Thread ID is " << threadID << "\n";
        srand(time(NULL));  
        int split = NUMBER_OF_OPERATIONS / NUMBER_OF_THREADS;   
        for (int i = threadID * split; i < (threadID * split) + split; i++) {
            if(randomNumbers[i] <= 0.5) {
                int numToAdd = rand() % 10 + 1;
                add(numToAdd);
            } 
            else {
                int numRemoved = remove();
            }
        }
    }

person John    schedule 08.03.2017    source источник


Ответы (1)


В этой строке вы захватываете i по ссылке:

myThreads[i] = std::thread ( [&] { bQ.doWork(randomNumbers, i); });

Это означает, что когда другой поток запускает лямбду, он получит самое последнее значение i, а не значение, когда оно было создано. Захватите его по значению вместо этого:

myThreads[i] = std::thread ( [&, i] { bQ.doWork(randomNumbers, i); });

Что еще хуже, поскольку у вас есть неупорядоченное чтение и запись в i, ваш текущий код имеет неопределенное поведение. И тот факт, что i, возможно, вышел за рамки основного потока до того, как другой поток прочитает его. Это исправление выше устраняет все эти проблемы.

person Mike Vine    schedule 08.03.2017
comment
Стоит явно указать, что существует задержка между созданием потока и выполнением функции потока. И конструктор потока возвращает управление, как только поток создается, поэтому вы можете фактически изменить множество переменных до того, как будет запущена функция потока. - person Revolver_Ocelot; 08.03.2017
comment
Это сделало это. Спасибо, Майк! - person John; 08.03.2017
comment
@Revolver_Ocelot Я новичок в многопоточном программировании. Является ли это источником беспокойства здесь? - person John; 08.03.2017
comment
@ Джон Да. Это причина, по которой захват по ссылке приводит к такому поведению. Кроме того, вы можете сделать это без лямбды, если хотите. Это будет работать так же: std::thread( decltype(bQ)::doWork, std::ref(bQ), randomNumbers, i ). - person Revolver_Ocelot; 08.03.2017