3

私は C++ 標準ライブラリを初めて使用し、特定のマルチスレッド実装に標準ライブラリ リストを使用しています。チュートリアル/ブログ/フォーラムの投稿で見たことのないリストを使用すると、トリックがある可能性があることに気付きました。私には明らかですが、誰も考慮していないようです。ですから、私は新人すぎて何かを見逃している可能性があるので、私より賢い誰かが私が達成しようとしていることを検証したり、私が間違っていることを説明したりしてくれることを願っています.

そのため、一般的に標準ライブラリ コンテナーはスレッド セーフではないことがわかっていますが、これはルールというよりも指針となるステートメントのように思えます。リストを使用すると、スレッドの安全性に対するある程度の許容範囲があるようです。説明させてください。リストに追加/削除しても、リストが無効にならないことはわかっています。無効化される唯一の反復子は削除された項目です。これは、次のコード行で修正できます。

it = myList.erase(it)

では、2 つのスレッドがあり、それらをスレッド 1 とスレッド 2 と呼びましょう。

スレッド 1 の役割は、リストに追加することです。それをキューとして扱うので、std::list::push_back() 関数呼び出しを使用します。

スレッド 2 の役割は、リストに格納されたデータをキューとして処理し、処理後にリストから要素を削除することです。

スレッド 2 は、処理中に追加されたばかりのリスト内の要素を削除しないことが保証され、スレッド 1 は、スレッド 2 の処理のために必要なデータをかなり前にキューに入れることを保証します。ただし、スレッド 2 の処理中に要素が追加される可能性があることに注意してください。

したがって、これは、データ保護のためにロックを使用せずに、このマルチスレッド環境でリストを使用するのが合理的であるように思われます。合理的であると私が言う理由は、基本的に、スレッド 2 は、次の擬似コードで示される現在の終了イテレータを取得できるように、現在までのデータのみを処理するためです。

Thread 2 {
    iter = myList.begin();
    lock();
    iterEnd = myList.end(); // lock data temporarily in order to get the current 
                            // last element in the list
    unlock();
    // perform necessary processing
    while (iter != iterEnd) {
        // process data
        // ...
        // remove element
        iter = myList.erase(iter);
    }
}

スレッド 2 は、処理を停止する場所を知るためだけに非常に短い時間ロックを使用しますが、ほとんどの場合、スレッド 1 とスレッド 2 は他のロックを必要としません。さらに、現在の最後の要素を知るスコープが柔軟であれば、スレッド 2 もロックを回避できる可能性があります。

誰かが私の提案に何か問題があると思いますか?

ありがとう!

4

1 に答える 1

5

あなたのプログラムは際どいです。1 つの明らかなデータ競合の例として: std::listは、二重にリンクされたノードの単なるコレクションではありません。また、たとえば、ノードの数をリストに格納するデータ メンバーもあります (単一のデータ メンバーである必要はありませんが、カウントをどこかに格納する必要があります)。

両方のスレッドがこのデータ メンバーを同時に変更します。これらの変更の同期がないため、プログラムはぎこちなくなります。

標準ライブラリ コンテナーのインスタンスは、外部同期なしで複数のスレッドから同時に変更することはできません。

于 2012-06-09T20:53:07.937 に答える