0

GameName (std::string) と、GameTime (size_t) のコレクションに対するイテレータの begin/end ペアを取る、次のテンプレート化された関数があります。範囲を反復処理し、GameTime-s を合計して、ゲーム名、合計ゲーム時間、平均ゲーム時間 (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 は、GameName を GameTime のベクトルにマッピングする std::map タイプです)

プログラムの実行からの出力の関連部分は次のとおりです。

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() を呼び出すことにより) 順次実行すると、プログラムは正しく実行されるため、各スレッドが何らかの理由で他のイテレーターを無効にしていることが推測されます。 、すべて値渡しされました。

この干渉の原因と、それらを並行して動作させる方法を知りたいです。

4

1 に答える 1

1

またはに置き換えconst std::pair<GameName, std::vector<GameTime> >&て、朝に電話してください。auto&&auto const&

マップに格納されているペアのタイプが正確に正しくないため、コードはループごとにベクトルをコピーします。 const&一時的にバインドでき、マップ内の型を使用した型に変換できます。一時にconst&直接バインドすると、参照の有効期間が延長され、一時は参照が範囲外になるまで存続します。

変換により、GameNameと の両方がコピーされstd::vector<GameTime>ます。

その後const&、ループの最後でスコープ外になり、一時的な it の有効期間が延長されます。はstd::vector<GameTime>破壊されます。通常、そのメモリは次のループ反復で再利用されます。

ご参考までに、あなたstd::map<Blah>::value_type

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

上記のコードは最初の引数を作成しませんconst。ただし、ここでの型は実際にはあまり有益ではありません。マップ内の型と正確に一致しない場合、コードは暗黙のうちにエラーを起こしやすくなります。そのため、これは を使用するのに最適な場所autoです。

for(:)その型への変換を実際に行う予定がある場合にのみ、ループ内でその型を指定してください。コピーを反復処理する場合は do for(auto x:y)、可変参照を超える場合は do 、変更していfor(auto& x:y)ないことを示したい場合は dofor(auto const& x:y)または (より良いが C++17) for(auto&& x:std::as_const(y))

そして、あまり気にせず、ただ効率的になりたい場合は、 を実行してくださいfor(auto&& x:y)

于 2016-11-01T20:28:00.687 に答える