web-dev-qa-db-ja.com

スレッド化-安全なstd:list C ++

私はマルチスレッドに不慣れで、いくつかのstd:listsをスレッドセーフにしようとしています。アイテムがリストに追加または削除されるたびに、mutex.lock()およびmutex.unlock()を実行するだけで十分でしょうか?繰り返しますが、私はそれらをスレッドセーフにしようとしているだけです。

ありがとう

6
Dark

安全のために、リストへのすべてのアクセスを保護する必要があります。ロックなしでリストから読み取ってもリストは破損しませんが、別のスレッドがリストから読み取っているときにリストを変更すると、いずれかのスレッドが破損する可能性があります(つまり、クラッシュしたり、誤った結果が生成されたりします)。

内容が安定していると予想されるコードのスパン全体にわたってロックを保持する必要があります。別のスレッドがいつでも要素を消去または並べ替えることができる場合、これには、そのコンテンツへのライブイテレータがあるときはいつでも含まれます。どのスレッドがどの要素を操作できるかに関して制限がある場合は、ライブイテレーターの保持に関してロック要件を緩和できます。

std :: lock_guard を使用すると、ロックを正しく管理できるようになります。リストを操作するスコープの最初にインスタンスを作成するだけで、スコープが例外によって終了した場合でも、スコープの最後に自動的にロックが解除されます。

8
nate

C++はthread-safeを定義していませんが、data raceを定義していることに注意してくださいこれは、複数のスレッドが同じオブジェクトにアクセスし、そのうちの少なくとも1つがライターである場合に発生する状態です。

ミューテックスを使用して、std::list<>データのメンバー関数を競合状態から解放できます。 BjarneStroustrupによるC++メンバー関数呼び出しのラッピング手法 を使用して、任意のオブジェクトに対してこれを行うこともできます。これは内部ロックとして知られており、オブジェクトが独自のミューテックスを維持することを意味します。

このメソッドは、参照、ポインター、イテレーターなど、オブジェクトの内部状態へのデータ競合のないリークされた参照を作成しません。たとえば、リストを反復処理するときに、要素を削除して別のスレッドがイテレータを無効にしないようにするため、反復が完了するまでミューテックスをロックしたままにする必要があります。

また、多くの有用なシナリオでは、アトミックに変更する必要があるのは複数のオブジェクトです。この場合、複数のオブジェクトへのアクセスをシリアル化する1つのミューテックスが必要です。

3

一般的に、はい。リストを変更または読み取るすべてのスレッド/パスが、そうする前に同じミューテックスをロックする場合、リストへのアクセスはスレッドセーフと見なすことができます。

誰かがイテレータ、参照、またはポインタを保持して、ロックの範囲外の項目をリストする場合は、警告が適用されることに注意してください。その場合、あなたはもはや安全ではありません。

1
Jesper Juhl

アイテムがリストに追加または削除されるたびに、mutex.lock()およびmutex.unlock()を実行するだけで十分でしょうか?

いいえ、データの読み取りを含むすべてのアクセスを同期する必要があります。読み取り/書き込みロックを使用して複数のリーダーのケースを最適化することもできますが、より複雑なロックに支払う価格は、特定の状況に応じて、得られるすべての利点を損なう可能性があります。

質問が「リスト内にミューテックスを入れてスレッドセーフにすることはできますか?」である場合。答えはノーです、できません。 std::listインターフェースを調べると、リストが保持するデータへのほとんどすべてのアクセスがイテレーターを介して行われていることがわかります。したがって、std::listスレッドを安全にするには、それらのイテレーターをスレッドセーフにする必要があります。削除されたデータを指すイテレータが逆参照された場合はどうしますか)。

0
Slava