2010-12-03 26 views
11

私はN要素のベクトルを持っていますが、このベクトルのn要素まで意味のあるデータを持っています。 1つのアップデータスレッドは、n番目またはn + 1番目の要素を更新し(n = n + 1に設定)、nがNに近すぎるかどうかをチェックし、必要に応じてvector :: resize(N + M)を呼び出します。更新後、スレッドは複数の子スレッドを呼び出してn番目のデータを読み込み、いくつかの計算を行います。STLベクトルとスレッドセーフ

子スレッドは決してデータを変更または削除しないことが保証されています(実際にデータが削除されることはありません)。アップデータは更新の終了直後に子を呼び出します。

これまでのところ問題は発生しませんでしたが、以前の更新から子作業スレッドが残っている場合は、大きなメモリブロックにベクトルを再割り当てする際に問題が発生するかどうか尋ねたいと思います。
このようなマルチスレッドの場合、スレッドセーフではないため、ベクターを使用するのは安全ですか?

EDIT: アップデーターがvector :: resize(N + M、0)を呼び出すときに挿入が行われるだけなので、私の問題の解決策はありますか? STLベクトルの優れた性能のために、私はそれをロック可能なベクトルに置き換えるつもりはないか、この場合、実行可能な、既知のロックフリーなベクトルはありますか?

答えて

19

以前の更新から子作業スレッドが残っている場合、大きなメモリブロックにベクトルを再割り当てする際に問題が発生するかどうかを確認したいと思います。

はい、これは非常に悪いです。

複数のスレッドからコンテナを使用しており、少なくとも1つのスレッドがコンテナの状態を変更する可能性があるアクションを実行する場合は、コンテナへのアクセスを同期させる必要があります。 std::vectorの場合

、そのサイズ(特に、挿入および消去)を変更するものは、(任意の挿入または削除を更新するstd::vectorの内部サイズのブックキーピングデータを必要とする)再配置が必要でない場合であっても、その状態を変更します。あなたの問題へ


一つの解決策は、プロデューサーが動的にstd::vectorを割り当て、それを所有するstd::shared_ptr<std::vector<T> >を使用して、消費者のそれぞれにこのstd::shared_ptrを与える持っていることであろう。

プロデューサがさらにデータを追加する必要がある場合、新しいstd::vectorから新しいサイズのより大きなサイズのコピーを動的に割り当てることができます。次に、新しい消費者をスピンオフしたり、新しいデータでコンシューマーを更新したりするときは、新しいstd::vectorstd::shared_ptrを与えるだけです。

+0

@James McNellis:はい。それは良いアドバイスです。私は自分自身を再配分することができます。実際には、ベクトルへのポインタを保持するクラス内でベクトルがラップされます。それはshared_ptrではありませんが、私は簡単に新しい大きなベクトルを構築し、古いものから要素をコピーして削除します。だから、大きなメモリブロックをコピーする最速の方法は何ですか。 CopyMemory()? –

+1

ベクトルの代わりに 'std :: deque'を使うほうが簡単な解決法ではないでしょうか?これにより、再配分は完全に回避されますが、ベクトルとほぼ同等のパフォーマンスが得られます。 – jalf

+0

@ jalf:再割り当てだけが問題ではないので、 'std :: deque'を使うのは安全だとは思いません。 'std :: deque :: operator []'がサイズやdequeの内部の他の簿記をチェックしないという保証はないので、消費者が 'operator []を呼び出す競合状態の可能性があります'は、プロデューサがデータを追加している間に内部状態を読み取り、内部状態を変更します。 –

1

あなたの従業員がデータスレッドセーフで作業する方法は?完成した労働者とプロデューサーの間に何らかの合図がありますか?そうでなければ、プロデューサーがベクトルをまだ動かしている間に移動させる可能性のある問題があることは間違いありません。代わりにstd::dequeに移動することでこれを修正することもできます(std::dequepush_backのイテレータを無効にしますが、要素への参照は影響を受けません)。

+0

@stonemetal:労働者と生産者の間に信号伝達はありません。どのようにしてdequeを使用するのですか? –

+0

@stonemetal:私はpush_back()でデータを挿入しません。サイズを変更してvec [n] = X;を呼び出します。これは問題ですか? –

+0

これは、どのように仕事を労働者に配っているかによって異なります。あなたは最後まで追記しているだけなので、push_backを使うか、必要に応じてサイズを変更して、前にプッシュしたりポップしたり、消去したりしないので、変わらないインデックスで作業者に作業を渡すことができます。要素が連続していることは保証されていませんが、配列のスタイルの開始と長さの設定ではなく、作業者が必要とする各要素へのポインタが必要です。イテレータはサイズ変更時に無効になり、オプションではありません。 – stonemetal

関連する問題