Функция, работающая с парой итераторов, не работает при параллельном запуске

У меня есть следующая шаблонная функция, которая принимает GameName (std::string) и пару итераторов начала / конца над коллекцией GameTime (size_t). Он перебирает диапазон и добавляет GameTime-ы вместе и возвращает кортеж из названия игры, общего игрового времени и среднего игрового времени (GameStats):

template<typename InputIt>
GameStats calculateGameStats(const GameName& _gameName, const InputIt _begin, const InputIt _end)
{
    std::string logMessage = "Started process for game " + _gameName + ":[ ";
    for_each(_begin,_end,[&logMessage](GameTime e){ logMessage += std::to_string(e) + ',';});
    std::clog << logMessage + " ]\n";

    size_t itemCount = 0;
    GameTime gameTime =
        std::accumulate(_begin,_end,0,[&itemCount](const GameTime _lhs, const GameTime _rhs)
    {
        ++itemCount;
        return _lhs + _rhs;
    });

    logMessage = "Ended process for game " + _gameName + ":[ ";
    for_each(_begin,_end,[&logMessage](GameTime e){ logMessage += std::to_string(e) + ',';});
    std::clog << logMessage + " ]\n";

    return std::make_tuple(_gameName, gameTime, gameTime / itemCount);
}

(Для целей отладки я также перечисляю элементы, которые мы повторяли в начале и в конце. Очевидно, они должны быть одинаковыми каждый раз, однако см. Ниже.)

Аналогичная версия этой функции, использующая ссылку на std::vector, работала нормально, однако эта новая, похоже, испортила свои итераторы, когда несколько ее экземпляров выполняются параллельно. Вот код, который запускает процесс для каждой игры:

// Start processing each game's stats in parallel
std::vector< std::future<GameStats> > processVector;
processVector.reserve(gameCount);
for(const std::pair<GameName, std::vector<GameTime> >& entryListPair : gameEntries)
{
    const std::string& gameName = entryListPair.first;
    const std::vector<GameTime>& entryList = entryListPair.second;
    processVector.push_back(std::async(std::launch::async,
                                       &calculateGameStats<decltype(entryList.cbegin())>,
                                       gameName,
                                       entryList.cbegin(),
                                       entryList.cend()));
    assert((processVector.cend()-1)->valid());
}

(GameEntries - это отображение типа std::map GameName на вектор GameTime)

Вот соответствующая часть вывода от запуска программы:

Started process for game CoD:[ 182,1264, ]
Ended process for game CoD:[ 606,1667, ]
Started process for game DotA:[ 606,1667, ]
Ended process for game DotA:[ 606,1667, ]
Started process for game GTAV:[ 606, ]
Ended process for game GTAV:[ 606, ]
Started process for game HotS:[ 606, ]
Ended process for game HotS:[ 606, ]
Started process for game LoL:[ 1277,193, ]
Ended process for game LoL:[ 1277,193, ]
Started process for game MC:[ 857,193, ]
Ended process for game MC:[ 857,193, ]
Started process for game OW:[ 0, ]
Note: 7 games in map, created 7 processes.
Ended process for game OW:[ 140377361861512, ]
Writing entry: CoD 2273 1136
Writing entry: DotA 2273 1136
Writing entry: GTAV 606 606
Writing entry: HotS 606 606
Writing entry: LoL 1470 735
Writing entry: MC 1050 525
Writing entry: OW 650759048 650759048
After processing: CoD:[ 1354,1442,]
After processing: DotA:[ 2137,1264,]
After processing: GTAV:[ 182,]
After processing: HotS:[ 2551,]
After processing: LoL:[ 606,1667,]
After processing: MC:[ 1277,193,]
After processing: OW:[ 857,]
Done!

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

Однако, как видно из выходных данных, повторение от (предположительно постоянного и неизмененного) начала и до конца в одной и той же функции каждый раз дает разные результаты. Это только в том случае, если задачи выполняются параллельно. Если запустить последовательно (вызывая wait() для каждого будущего перед запуском следующего), программа запускается правильно, поэтому я предполагаю, что каждый поток по какой-то причине лишает законной силы итераторы других, даже если они являются входными итераторами для разных векторов, которые были все переданы по значению.

Я хотел бы знать, что вызывает это вмешательство и как я мог заставить их работать параллельно.

1 ответ

Решение

Замещать const std::pair<GameName, std::vector<GameTime> >& с auto&& или же auto const& и позвони мне утром.

Ваш код копирует вектор в каждом цикле, потому что вы получили неверный тип пары, сохраненной на карте. const& можно привязать к временному, а тип на карте можно преобразовать в используемый вами тип. когда const& непосредственно связывается с временным, вы получаете продление срока действия ссылки, и временное действие длится до тех пор, пока ссылка не выйдет из области видимости.

Конверсия копирует оба GameName и std::vector<GameTime>,

const& затем выходит из области видимости в конце цикла, увеличивая временное время его жизни. std::vector<GameTime> уничтожен Обычно его память затем повторно используется на следующей итерации цикла.

Если вам интересно, ваш std::map<Blah>::value_type является

std::pair<const GameName, std::vector<GameTime> >

в то время как ваш код выше не делает первый аргумент const, Тем не менее, типы здесь на самом деле менее информативны, код будет молча подвержен ошибкам, если они не будут точно совпадать с типами на карте, и, как таковой, это идеальное место для использования. auto,

Указывать только тип в for(:) цикл, если вы действительно намереваетесь вызвать преобразование в этот тип. Если вы хотите перебрать копии, сделайте for(auto x:y), если над изменчивыми ссылками сделать for(auto& x:y), если вы хотите заявить, что вы не мутируете или делать for(auto const& x:y) или (лучше, но C++17) for(auto&& x:std::as_const(y)),

И если вам не все равно, а просто хотите быть эффективным, сделайте for(auto&& x:y),

Другие вопросы по тегам