运行 并行时采用迭代器对的函数不起作用

Function taking iterator pair not working when run in parallel

我有以下模板化函数,它在 GameTime (size_t) 集合上接受一个 GameName (std::string) 和一对 begin/end 迭代器。它遍历范围并将 GameTime-s 加在一起,以及 returns 游戏名称、总游戏时间和平均游戏时间 (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 条目,以确保它没有被修改,以防万一这就是问题所在,但向量都毫发无损地出来了。

然而,从输出中可以看出,在同一个函数中从(假定为常量且未修改的)开始迭代到结束每次都会产生不同的结果。仅当任务 运行 并行时才会出现这种情况。如果 运行 顺序(通过在启动下一个 future 之前调用 wait() ),程序 运行s 是正确的,所以我的猜测是每个线程由于某种原因使其他线程的迭代器无效,即使它们是不同向量的输入迭代器,它们都是按值传递的。

我想知道造成这种干扰的原因以及如何让它们并行工作。

auto&&auto const& 替换 const std::pair<GameName, std::vector<GameTime> >& 并在早上给我打电话。

您的代码每次循环都会复制向量,因为您得到的存储在地图中的对的类型不完全正确。 const&可以绑定到temporary,map里面的类型可以convert-成你用的类型。当 const& 直接绑定到一个临时文件时,您将获得引用生命周期延长,临时文件将持续到引用超出范围。

转换复制 GameNamestd::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)