2017-10-26 7 views
0

私は、次のサンプルコードは、スレッドセーフであるかどうかを判断しようとしている:のstd ::マップスレッドの安全性とイテレータの無効

std::map<K, V> myMap; 

void foo() { 
    myMap[k]; // Construct a new key-value pair with key `k`. 
} 

void bar() { 
    auto it = myMap.find(x); 
    if (it != myMap.end()) { 
     std::lock_gaurd<std::mutex> lg(...); 
     // do something with *it 
    } 
} 

(プログラムが終了するまで、少なくとも)起こって何の削除はありません唯一の操作は、fooに要素を追加し、barにそれらを反復することです。関数fooは1つのスレッドから呼び出され、barは同時に複数のスレッドで呼び出されます。

今、STLコンテナはスレッドセーフではないことを理解しており、myMap::operator[]を使用して非const演算を同時に実行しています。私が混乱しているのは、STL findは基になるマップを変更しないためで、myMap::operator[]はイテレータや参照を無効にしないため、安全ではないと考えられますか? operator[]は非constなので、私はデータ構造を同時に変更しているか、またはイテレータを無効にしないので安全だと考えることができるので心配する必要はありませんか?

+0

不変量は非const関数を呼び出す前後で有効ですが、操作中に*有効であるとは限りません。 –

答えて

2

あなたは疑わしいことが正しいです。異なるスレッドから同時にmyMap[k]myMap.find(x)を呼び出すことはできません。コンテナと

、あなたは安全に一度、次のいずれかの実行することができます。いずれかから

  1. コールのconstメンバ関数を並行スレッドの数、または

  2. は非constを呼び出しメンバ関数を正確に1つのスレッドから削除します。 operator[]以来

あなたはミューテックスとそれを守るか、そうでなければ、同時に他のメンバ関数を呼び出していないことを確認する必要があり、constではありません。

+0

追加: 'foo()'を同時に実行すると 'bar()'が遅すぎるので、lock_guardを 'foo()'に追加するだけでは十分ではありません。例: 'foo()'はロックを保持することができます。 'bar()'はまだ '.find()'を実行していて、 'foo()'はマップに何かを挿入します。 ' - >おそらくクラッシュする –

+0

"バーのそれを無効にする "。これはおそらく私の混乱の原因です。 http://en.cppreference.com/w/cpp/container/map/operator_atによると、 "イテレータや参照は無効になりません。"それは無効ではないでしょうか? –

+1

@AnthonyCalandra:正解、 'それは無効にされません。しかし、あなたは 'myMap.find(x);をロックし、' myMap.end() 'をロックしなければなりません(安全かもしれませんが、標準ではそうは言いません!)分割した各セクションを個別にロックするのではなく、その時点で関数全体をロックします。 –

関連する問題