同時に書き込まれるオブジェクトが多数ありますが、変化する可能性があります。そのアクセスをミューテックスで保護したい。そのために、私は_std::vector<std::mutex>
_を使用すると思っていましたが、std::vector::resize()
が必要とする一方で、_std::mutex
_にはコピーまたは移動コンストラクタがないため、これは機能しません。
この難問に対する推奨される解決策は何ですか?
編集:すべてのC++ランダムアクセスコンテナーは、サイズ変更のためにコピーまたは移動コンストラクターを必要としますか? std :: dequeは役に立ちますか?
再編集
まず、あなたのすべての考えに感謝します。ミューティックスを回避したり、ミューティックスをオブジェクトに移動したりするソリューションには興味がありません(詳細や理由は示しません)。したがって、ミューテックスの数を調整できるようにしたい(ミューテックスがロックされていないときに調整が行われることが保証されている)場合、いくつかの解決策があるようです。
1固定数のmuticesを使用し、ハッシュ関数を使用してオブジェクトからmuticesにマッピングできます(Oblivous船長の回答のように)。これにより衝突が発生しますが、ミューティックスの数がスレッドの数よりはるかに多いが、オブジェクトの数よりも少ない場合、衝突の数は少なくなるはずです。
2(ComicSansMSの回答のように)ラッパークラスを定義できます。
_struct mutex_wrapper : std::mutex
{
mutex_wrapper() = default;
mutex_wrapper(mutex_wrapper const&) noexcept : std::mutex() {}
bool operator==(mutex_wrapper const&other) noexcept { return this==&other; }
};
_
_std::vector<mutex_wrapper>
_を使用します。
_std::unique_ptr<std::mutex>
_を使用して、個々のミューテックスを管理できます(マティアスの回答のように)。このアプローチの問題は、各mutexがヒープ上で個別に割り当ておよび割り当て解除されることです。したがって、私は好む
4std::unique_ptr<std::mutex[]> mutices( new std::mutex[n_mutex] );
特定の数の_n_mutex
_のmuticesが最初に割り当てられたとき。この数が後で不十分であると判明した場合、私は単に
_if(need_mutex > n_mutex) {
mutices.reset( new std::mutex[need_mutex] );
n_mutex = need_mutex;
}
_
それで、これらの(1,2,4)のどれを使うべきですか?
vector
は、値が大きくなるにつれて連続する値の配列を維持するために、値が移動可能である必要があります。 mutexを含むベクターを作成することはできますが、サイズを変更する必要があるようなことは何もできません。
他のコンテナにはその要件はありません。構築中に、またはemplace()
またはresize()
を使用してミューテックスを適切に構築する限り、deque
または_[forward_]list
_のいずれかが機能します。 insert()
やPush_back()
などの関数は機能しません。
または、間接的なレベルを追加して_unique_ptr
_を格納することもできます。しかし、別の回答でのあなたのコメントは、ダイナミックアロケーションの追加コストは許容できないと考えていることを示しています。
std::unique_ptr<std::mutex>
の代わりに std::mutex
。 unique_ptr
sは可動です。
特定の長さを作成する場合:
std::vector<std::mutex> mutexes;
...
size_t count = 4;
std::vector<std::mutex> list(count);
mutexes.swap(list);
固定ミューテックスプールを使用することをお勧めします。 std::mutex
の固定配列を保持し、ハッシュテーブルの場合と同様に、オブジェクトのアドレスに基づいてロックする配列を選択します。
std::array<std::mutex, 32> mutexes;
std::mutex &m = mutexes[hashof(objectPtr) % mutexes.size()];
m.lock();
hashof
関数は、ポインター値を数ビットでシフトする単純なものにすることができます。この方法では、ミューテックスを一度初期化するだけで済み、ベクターのサイズ変更のコピーを回避できます。
効率がこのような問題である場合、非常に頻繁に変更される非常に小さなデータ構造しかないと思います。その場合、ミューテックス、具体的にはstd::atomic_compare_exchange_strong