2012-11-20 1 views
7

私は、スレッド安全のためにプロビジョニングされたロックと並んで、複数のスレッドによってアクセスされるデータを組み合わせる方法を検討してきました。私はconst-correctnessを維持しながらこれを行うことはできないと思っています。私はmutableとしてmutex_をマークし、データとロックを組み合わせてみてください。この中データを結合してロックしているとき、const-correctはできませんか?

template <typename TType, typename TMutex> 
class basic_lockable_type 
{ 

public: 
    typedef TMutex lock_type; 

public: 
    template <typename... TArgs> 
    explicit basic_lockable_type(TArgs&&... args) 
     : TType(std::forward<TArgs...>(args)...) {} 

    TType& data() { return data_; } 
    const TType& data() const { return data_; } 

    void lock() { mutex_.lock(); } 
    void unlock() { mutex_.unlock(); } 

private: 
    TType   data_; 
    mutable TMutex mutex_; 

}; 

typedef basic_lockable_type<std::vector<int>, std::mutex> vector_with_lock; 

は、例えば以下のクラスを取ります。残念ながら、使用時には、完全には正しくないconst関数から読み取る操作を実行するためにmutableとマークする必要があるため、これは十分ではありません(data_はconstからのmutableである必要があります) 。

void print_values() const 
{ 
    std::lock_guard<vector_with_lock> lock(values_); 
    for(const int val : values_) 
    { 
     std::cout << val << std::endl; 
    } 
} 

vector_with_lock values_; 

とにかくconst-correctnessが維持され、データとロックが結合されるような人は誰でも見ることができますか?また、私はここで誤った仮定をしていますか?

+2

'lock'と' unlock'をconstにしますか? (また、それはすべきではない 'のstd :: lock_guard あなたがそれを使用しない場合のために、新たなロック可能な作ったのはなぜ?'?) –

+0

更新 'マーキング、もちろんR.MartinhoFernandes @ – Graeme

+0

をlock_guard'これらconstは、変更不可能な 'vector_with_lock'インスタンスコール' lock'と 'unlock'を許可します、はい?あなたは、私が答え – Graeme

答えて

6

個人的には、私は手動でロックする必要がなく、最初にロックすることなく実際にはアクセスできない方法でデータをカプセル化することをお勧めします。

友人の機能applyまたはロックを行い、カプセル化されたデータを取得し、その中に保持されたロックを使用して実行される関数オブジェクトに渡すオプションがあります。

//! Applies a function to the contents of a locker_box 
/*! Returns the function's result, if any */ 
template <typename Fun, typename T, typename BasicLockable> 
ResultOf<Fun(T&)> apply(Fun&& fun, locker_box<T, BasicLockable>& box) { 
    std::lock_guard<BasicLockable> lock(box.lock); 
    return std::forward<Fun>(fun)(box.data); 
} 
//! Applies a function to the contents of a locker_box 
/*! Returns the function's result, if any */ 
template <typename Fun, typename T, typename BasicLockable> 
ResultOf<Fun(T const&)> apply(Fun&& fun, locker_box<T, BasicLockable> const& box) { 
    std::lock_guard<BasicLockable> lock(box.lock); 
    return std::forward<Fun>(fun)(box.data); 
} 

使用は、その後になる:

void print_values() const 
{ 
    apply([](std::vector<int> const& the_vector) { 
     for(const int val : the_vector) { 
      std::cout << val << std::endl; 
     } 
    }, values_); 
} 

または、範囲に基づく適切スコープロックのforループ乱用および「単一」動作と値を抽出することができます。

for(auto&& the_vector : box.open()) { 
    // lock is held in this scope 
    // do our stuff normally 
    for(const int val : the_vector) { 
     std::cout << val << std::endl; 
    } 
} 

私は説明が順序であると思う:必要とされているすべてのイテレータの適切なセットです。一般的な考え方は、open()がRAIIハンドルを返して、構築時にロックを取得し、破壊時に解放するということです。範囲ベースのforループは、そのループが実行されている間、この一時的な存続を保証します。これにより、適切なロック範囲が得られます。

このRAIIハンドルは、単一の値を持つ範囲のbegin()end()イテレータも提供します。これが保護されたデータを得る方法です。範囲ベースのループは、逆参照を行い、ループ変数にバインドします。範囲はシングルトンなので、実際には常に「ループ」が1回だけ実行されます。

boxは、実際にインターロックされたアクセスを強制するように、他の方法でデータを取得するべきではありません。

もちろん、ボックスが開いたら、参照がボックスが閉じられた後に利用できるように、データへの参照を格納することができます。しかしこれはマキアヴェッリではなく、マーフィーを守るためです。

構造が奇妙に見えるので、私は誰もそれが欲しいと非難しません。片方の手はセマンティクスが完璧なのでこれを使用したいが、一方ではこれは範囲ベースのものではないので、私はしたくない。握り手では、このレンジRAIIハイブリッド技法はかなり一般的で、他の目的のために容易に虐待されるかもしれませんが、私はそれをあなたの想像力/悪夢に任せます;)あなた自身の裁量で使用してください。読者のための運動が、イテレータのような一連の短い例は自分locker_box implementationに見ることができるように、左


。ミューテックスは、ロックされているかどうか意味で

+0

O_oの 'for'はかなり乱暴です。 – Mankarse

+0

そして私は私の実装でバグに気づいた:S Ooops。自宅で、そのコードを使用しないでください。 –

+0

まだバグですか?もしそうなら、どんな理由で? – sehe

3

"const correct"で何を理解していますか?一般に、私は、mutexがオブジェクトの論理(または観測可能な)状態の一部でない場合、それを宣言することに何も問題がないことを意味する論理constのコンセンサスがあると思います。 。

0

、またはしない物体の観察状態の一部である - 誤ってロックインバージョンを作成することにより、例えばそれを観察することができます。セルフロックオブジェクトとの根本的な問題であり、私はそれの一つの側面は、constの-正しさに関係ないと思います

あなたは、参照ツーのconstを経由して、オブジェクトの「lockedness」を変更することができ、または他のあなたは、参照ツーのconstを経由して同期のアクセスをすることはできませんどちらか。おそらく最初のものを選んでください。

代替はlockednessが観察状態の一部ではないように、オブジェクトは、ロックされた状態の間、呼び出しコードによって「観察」することができないことを保証することです。しかし、呼び出し元がそれぞれの要素を単一の同期操作としてvector_with_lockに訪れる方法はありません。ロックを保持しているユーザーのコードを呼び出すとすぐに、ロックが保持されているかどうかを確認する潜在的な、または保証されたロックの逆転を含むコードを書くことができます。コレクションの場合、これで問題は解決されません。

関連する問題