2016-04-08 9 views
0

標準のベクトルを囲む単純なラッパーを指定すると、通常どおりコンテンツを設定できるようにスレッドセーフな方法でoperator[]を実装する良い方法はありますか?ここでC++スレッドセーフなブラケットオペレータプロキシ

struct bracket_operator_proxy; 

struct example 
{ 
    auto operator[](size_t i) const { return bracket_operator_proxy(v, m, i); } 
private: 
    std::vector<double> v; 
    std::mutex m; 
}; 

bracket_operator_proxyのための私の迅速かつ素朴な試みです:

struct bracket_operator_proxy 
{ 
    bracket_operator_proxy(std::vector<double>& v, std::mutex& m, int i) 
     : v(v), m(m), i(i) {} 

    operator double() const 
    { 
     std::lock_guard<std::mutex> l(m); 
     return v[i]; 
    } 

    auto operator=(double d) 
    { 
     std::lock_guard<std::mutex> l(m); 
     v[i] = d; 
     return d; 
    } 

    //... further assignment operators ... 
private: 
    std::vector<double>& v; 
    std::mutex& m; 
    int i; 
}; 

はこのもう十分ですか?または私は私の足を吹き飛ばす何かを逃していますか?

+0

現在、あなたは 'operator_'が 'bracket_operator_proxy 'ではなく' double'を返すように '(example [i] = 4.2)= 42;'を禁止しています。落とし穴ではありません。 – Jarod42

+0

@ Jarod42:はい、その意図でした。さもなければ、デストラクタでmutexのロックを解除する必要があると思います。より良い選択肢のための任意のアイデア? – davidhigh

+0

mutexは 'operator ='の最後にロックされていないので、 'bracket_operator_proxy 'を返すことはOKです。 BTW 'std :: vector >'は良い選択肢のようです。 – Jarod42

答えて

1

operator->(これは非常に便利です)を使用すると、->プロキシを返す必要があります。このプロキシは、ステートメントの終わりまでロックの有効期間を延長し、シングルスレッドデッドロックにさらされます。

the one hereのようなスレッドセーフなモナド/ファンクタ/ラッパーを見てください。ロックを完全に透明にするわけではありませんが、は、であってはなりません。あなたは、データを共有することが変異しなければならない場合

  • それは不変にする、既知の安全なデザインのボトルネックを通じてアクセスを分離した場合

    • は、スレッド

    • の間でデータを共有しないでください。メッセージキューは言う。

    • あなたがそれを行うことができない場合は、本当に

    • を再設計を検討してください。原子多分?

    • は、明示的に

    • [OK]を、ロックを管理する機能の限られたセット、魔法のようにロックを取得し、今簡単っぽい化合物の操作で、上記のようにリーダ/ライタモナドで包ん

    • メイクコードを持って、非スレッドインタラクションコードのように見えるので、読者はセキュリティと効率性の誤った感覚に惑わされます。

    スレッドセーフの危険で難しい部分は、構文が厄介であるという事実ではありません。ロックベースのスレッドセーフティは、正確かつ安全であることを証明することはほとんど不可能です。構文を使いやすくすることは、価値の高い目標ではありません。

    例として、v[i]=v[i+1]は、同期が不足しています。読み取りと書き込みの間に何かが変化している可能性があります。 "iは有効なインデックスですか?"

  • +0

    あなたの答え、スレッドセーフラッパーへのリンクとそれが間違っている可能性がある例をありがとう。どういうわけか、マルチスレッドが醜いものでなければならないかのように私には思われます... – davidhigh

    +0

    私は、参照の代わりにポインタを使用し、妥当な[コピーコンストラクタ](https://www.justsoftwaresolutions.co.uk/threading/thread-safe -copy-constructors.html)とクラス代入演算子は、スレッドセーフになっていますか? (あなたが知っていることは、証明することはほとんど不可能だと知っていますので、あなたの直感に従ってください)。問題は私のlibのさまざまな場所ですでに 'operator []'を使用していて、ユーザーが機能を拡張することを可能にすることです。 – davidhigh

    +0

    @davidhigh他のスレッドが物を修正している間に様々な場所でロックを取得して解放するシングルスレッドコードとマルチスレッドコードは、基本的に異なる獣です*。 'a [i] = a [i-1] + 1'は、シングルスレッドコードとはまったく異なる意味を持っています(シングルスレッドポスト条件は' a [i] a [i-1] 'であるが、マルチスレッドコードでは真ではない*)。既存のコードをコンパイルすることは、マルチスレッドコードを書く恐ろしい方法です。また、1つ以上のロック、またはリエントラントでないロックがある場合は、ほとんどロックされます。 – Yakk

    関連する問題