B
クラスがインスタンス化され、複数のスレッドによって使用される、次の C++11 コードを検討してください。共有ベクトルを変更するためB
、 の ctor およびメンバー関数 foo でアクセスをロックする必要がありB
ます。メンバー変数を初期化するid
には、複数のスレッドからアクセスするため、アトミック変数であるカウンターを使用します。
struct A {
A(size_t id, std::string const& sig) : id{id}, signature{sig} {}
private:
size_t id;
std::string signature;
};
namespace N {
std::atomic<size_t> counter{0};
typedef std::vector<A> As;
std::vector<As> sharedResource;
std::mutex barrier;
struct B {
B() : id(++counter) {
std::lock_guard<std::mutex> lock(barrier);
sharedResource.push_back(As{});
sharedResource[id].push_back(A("B()", id));
}
void foo() {
std::lock_guard<std::mutex> lock(barrier);
sharedResource[id].push_back(A("foo()", id));
}
private:
const size_t id;
};
}
残念ながら、このコードには競合状態が含まれており、このようには機能しません (ctor と foo() が同じ ID を使用しない場合があります)。id の初期化をミューテックスによってロックされている ctor 本体に移動すると、次のように動作します。
struct B {
B() {
std::lock_guard<std::mutex> lock(barrier);
id = ++counter; // counter does not have to be an atomic variable and id cannot be const anymore
sharedResource.push_back(As{});
sharedResource[id].push_back(A("B()", id));
}
};
後者の例が機能する理由を理解するのを手伝ってもらえますか (同じミューテックスを使用していないためですか?)? ctorの本体でロックせずid
に、イニシャライザリストで初期化する安全な方法はありますか? B
私の要件は、そうでid
なければならずconst
、の初期化がid
初期化子リストで行われることです。