2016-03-12 14 views
12

std::mapが必要なプログラム、特にmap<string,map<string,int>>のようなプログラムを作成しています - これは銀行の変更率のようなものです。最初の文字列は元の通貨で、もう1つは2番目のマップ望ましいものでintはその割合です。この全体の地図はです。読み取り専用です。私はまだミューテックスが必要ですか?これは私の最初の大きなマルチスレッドプログラムなので、スレッドの安全性については少し混乱しています。C++スレッドの安全性 - 地図の読み込み

+1

私はあなた 'map'がある場合は、スレッドのいずれかがそれにアクセスする前に、すべてのそれは、あなたは罰金だ、それに挿入された要素だと考えています。 – Nard

+0

この地図はどのように記入されますか?コンパイル時に?実行時に読み込みトレッドが始まる前に?実行時に読み込みスレッドと同時に? – zaratustra

答えて

12

std::mapについて話していて、スレッドに書き込むスレッドがない場合、同期は必要ありません。書き込みのない同時読み込みは問題ありません。

ただし、少なくとも1つのスレッドがマップに書き込みを実行する場合は、実際にはミューテックスのような保護が必要です。

std::map::operator[]は書き込みとカウントされるので、代わりにstd::map::at(またはキーがマップに存在しない場合はstd::map::find)を使用してください。 const map&を介して共有マップを参照するだけで、コンパイラがあなたの偶発書き込みからあなたを守るようにすることができます。†


OPにおけるケースのように明らかにしました。他のクラスにはmutableのメンバーがいるかもしれないことに注意してください。そのためには、const&を経由してアクセスすることも、レースを導入する可能性があります。疑わしい場合は、マニュアルをチェックするか、パラレルプログラミングのために別のものを使用してください。

+0

OK、そのオペレータのチップのおかげで、便利になるでしょう。ちょうど、ところで、なぜ演算子はこのように動作しますか? –

+0

@ Jesse_Pinkman要素が見つからない場合は要素を挿入しますが、これは明らかにマップを変更します。 –

3

あなたが共有データを持っていて、少なくとも1つのスレッドがライターになり、次に同期が必要な場合、経験則があります。スレッドの1つがライターである場合、書き込みをしている要素を読み手に読み取らせたくないので、同期が必要です。これは、読者が古い値の一部と新しい値の一部を読み取る可能性があるため、問題を引き起こす可能性があります。

すべてのスレッドはデータを読み取っているだけなので、マップに影響を与えることはできませんので、並行(非同期)読み取りを行うことができます。

1

両方のマップが常にREADONLYであることが確実であれば、ミューテックスは必要ありません。

ただし、プログラムの実行中に誰も地図を更新できないように注意する必要があります。プログラムの初期段階でマップを初期化していることを確認し、何らかの理由で更新しないでください。

あなたが混乱している場合は、プログラムの実行の間にそれを更新する必要があるかもしれませんし、地図のまわりにマクロがある方がよいでしょう。そして、将来、それらの周りにmutexが必要な場合は、マクロ定義を変更してください。

PS ::私は共有リソースで簡単に置き換えることができる答えでマップを使用しました。理解の容易さのため

2

std::map<std::string, std::map<std::string,int>> constconstメンバー関数[*]のカスタムクラスにラップします。

これは、作成後にクラスのオブジェクトを使用するすべてのスレッドがそのスレッドから読み込みを行い、C++ 11以降で安全であることを保証します。documentationとして

は言う:

すべてconstメンバ関数は、同じコンテナ上の異なる のスレッドで同時に呼び出すことができます。

あなた自身のカスタムタイプのラッピングコンテナはとにかく良い練習です。スレッドの安全性の向上は、その優れた実践の唯一のプラスの副作用です。その他の肯定的な影響としては、クライアントコードの可読性の向上、必要な機能へのコンテナインターフェイスの縮小/適合、追加の制約の追加やチェックの容易さなどがあります。ここで

は簡単な例です。いずれにせよ

class BankChangeRates 
{ 
public: 

    BankChangeRates(std::map<std::string, std::map<std::string,int>> const& data) : data(data) {} 

    int get(std::string const& key, std::string const& inner_key) const 
    { 
     auto const find_iter = data.find(key); 
     if (find_iter != data.end()) 
     { 
      auto const inner_find_iter = find_iter->second.find(inner_key); 
      if (inner_find_iter != find_iter->second.end()) 
      { 
       return inner_find_iter->second; 
      } 
     } 
     // error handling 
    } 

    int size() const 
    { 
     return data.size(); 
    } 

private: 
    std::map<std::string, std::map<std::string,int>> const data; 
}; 

、スレッド安全性の問題は、その後、コンストラクタは、別のスレッドが書き込み先のオブジェクトから読み取るいないことを確認するために、どのように削減されます。これはしばしば簡単に達成されます。例えば、オブジェクトは、マルチスレッドが開始される前に構築されてもよいし、ハードコードされた初期化リストによって初期化されてもよい。多くの場合、オブジェクトを作成するコードは一般に、他のスレッドセーフな関数とローカルオブジェクトにのみアクセスします。

オブジェクトへの同時アクセスは、一度作成されると常に安全です。もちろん


[*] constメンバ関数は、その約束を守るとmutableまたはconst_castで "回避策" を試みるべきではありません。

関連する問題