1

読み取り/書き込みのロックを回避するために、次の方法でベクトルなどのSTLコンテナーを反復処理しても安全かどうか疑問に思いますが、「書き込み」スレッドによってのみpush_back()操作を許可します。

for (size_t i = 0; i < vec.size(); i++)
{
   const T& t = *vec[i];
   // do something with t
}

コンテナーの変更によってイテレーターが無効になる可能性があることは理解していますが、初期コンテナーサイズが将来の追加に十分な大きさであることを確認した場合、読み取りまたは書き込みをロックせずに要素を反復処理しても安全であるはずです。

4

3 に答える 3

3

読み取り/書き込みのロックを回避するために、次の方法でベクトルなどのSTLコンテナーを反復処理しても安全かどうか疑問に思いますが、「書き込み」スレッドによってのみpush_back()操作を許可します。

しないでください、これはスレッドセーフではありません。値を読み取ろうとして現在のバッファーに移動しようとしているスレッドについて考えてみます。この時点で、2番目のスレッドがバッファーを拡張しており、最初のスレッドがバッファーへのポインターを取得した後、実際に値を読み取る前に、2番目のスレッドがバッファーの内容を解放します。最初のスレッドはデッドオブジェクトの読み取りですが、これは未定義の動作です。

問題を-edベクトルに制限するreserve()(つまり、バッファーの増大の問題を回避する)アプローチは、依然としてスレッドセーフではありません。のコードは次のpush_back()ようになります。

template <...>
class vector {
   T *begin,*end,*capacity;   // common implementation uses 3 pointers

   void push_back(T value) {
       if (end == capacity) { /* grow vector and insert */ }
       else {
          new (end) T(value);
          ++end;
       }
   }
};

ここでの問題は、同期メカニズムがないと、コンパイラが命令を並べ替え、インクリメントendしてメモリに格納Tし、バッファ要素に対してのコンストラクタを呼び出すことができることです。その並べ替えが発生した場合、リーダースレッドにはsize()、現在保存されている値を含む値が表示される可能性がありますが、その値はまだベクターに含まれていません。

于 2013-03-12T20:23:40.423 に答える
0

実装の詳細としてこれに依存するコードを書くべきではなく、私が知る限り、ベクターのエクスポートされたAPIの一部ではありません。文書化された動作の場合は信頼できますが、そうでない場合は信頼できません。実装に依存し、APIの文書化された部分ではないものはすべて、異なるプラットフォームおよび同じプラットフォーム上のツールの異なるバージョンで変更される可能性があります。

また、@ GManNickGのコメントから->ここでロックせずに変更および読み取りされるため、size()の呼び出しで競合状態が発生します。

于 2013-03-12T19:45:12.750 に答える
0

イテレータが無効にならないという提案に頼ることはできません(多くのスペースが残っています)。そして、これにはshared_mutexとshared_lockが必要です。(使用例

于 2013-03-12T21:01:57.197 に答える