22

Stackoverflow、STL コンテナーはどれも書き込みに対してスレッドセーフではないことを読みました。しかし、それは実際には何を意味するのでしょうか? 書き込み可能なデータをプレーン配列に格納する必要があるということですか?

ベクトルのサイズ変更が必要になる可能性があるため、同時呼び出しによりstd::vector::push_back(element)データ構造の一貫性が失われる可能性があると思います。しかし、サイズ変更が含まれていない次のような場合はどうでしょうか。

  1. 配列の使用:
int data[n];
// initialize values here...

#pragma omp parallel for
for (int i = 0; i < n; ++i) {
    data[i] += func(i);
}
  1. `std::vector` を使用:
std::vector<int> data;
data.resize(n);
// initialize values here...
    
#pragma omp parallel for
for (int i = 0; i < n; ++i) {
    data[i] += func(i);
}

最初の実装は、a) スレッドセーフの点で、および b) パフォーマンスの点で、2 番目の実装よりも本当に優れていますか? 私は C スタイルの配列に慣れていないので、std::vector を使用したいと思います。

編集:#pragma omp atomic update書き込みの保護を削除しました。

4

4 に答える 4

27

2 つは同じように安全です。複数のスレッドからアクセスされる要素がなければ、問題ありません。並列ループは各要素に 1 回だけアクセスするため、1 つのスレッドからのみアクセスします。

標準には、コンテナーのメンバー関数を非スレッドセーフにする余地があります。この場合は を使用するvector<int>::operator[]ため、そのメンバーのスレッドセーフを明示的に保証する必要があります。これは、const 以外のベクトルで呼び出してもベクトル自体が変更されないため、妥当と思われます。したがって、この場合に問題があるとは思えませんが、保証を探していません [編集: rici が見つけました]。安全でない可能性がある場合でもint *dataptr = &data.front()、ループの前に実行してから、dataptr代わりにインデックスをオフにすることができますdata

余談ですが、このコードは、1 つのオブジェクト内に複数の要素が共存する特殊なケースであるため、安全であるとは限りません。の異なる要素は異なる「メモリ位置」(C++11 では 1.7) であるvector<bool>ため、 の配列に対しては安全です。bool

于 2012-12-19T15:33:21.617 に答える
18

データ競合のルールを規定する c++11 では、コンテナーのスレッドセーフについて説明します。標準の関連するセクションは、§ 23.2.2、パラグラフ 2 です。

(17.6.5.9)にもかかわらず、同じシーケンス内の異なる要素 (vector<bool> を除く) に含まれるオブジェクトのコンテンツが同時に変更される場合、実装はデータ競合を回避する必要があります。

[ 注: サイズが 1 より大きい vector<int> x の場合、x[1] = 5 と *x.begin() = 10 はデータ競合なしで同時に実行できますが、x[0] = 5 と * x.begin() = 10 を同時に実行すると、データ競合が発生する可能性があります。一般的なルールの例外として、vector<bool> y の場合、y[0] = true は y[1] = true と競合する可能性があります。—終わりのメモ]

前述の§ 17.6.5.9 は、特に許可されていない限り、標準ライブラリ インターフェイスによる同時変更を基本的に禁止しているため、引用したセクションでは、何が許可されているか (および使用法を含む) を正確に示しています。

[]この問題は Steve Jessop によって提起されたため、§ 23.2.2 のパラグラフ 1 では、シーケンス コンテナーの同時使用が明示的に許可されています。

データ競合 (17.6.5.9) を回避する目的で、実装は次の関数を const と見なす必要があります: begin、end、rbegin、rend、front、back、data、find、lower_bound、upper_bound、equal_range、at and、連想を除くまたは順序付けられていない連想コンテナー、operator[]。

于 2012-12-19T15:39:24.843 に答える
3

これが意味する主なことは、ベクターにアクセスする複数のスレッドがある場合、複数の同時書き込みでデータ構造が破損するのを防ぐためにC++に依存することはできないということです。したがって、何らかのガードを使用する必要があります。一方、例ではそうではないように、プログラムが複数のスレッドを使用していない場合は、まったく問題ありません。

于 2012-12-19T15:30:54.330 に答える
1

この場合、必要な数の値を使用してベクトルを構築する必要がありますか? そして、すべてうまくいきます。

std::vector<int> data(n, 0);

resize()にうまく機能します。性能は同等になります。マルチスレッド アクセスでベクトルが破損しない理由は、データがその場所にあり、そこから移動しないためです。OMP スレッドが一度に同じ要素にアクセスすることはありません。

于 2012-12-19T15:32:04.463 に答える