2016-12-17 9 views
0

私のメソッドのメソッドを同期させて実行したい。ガードを使用する際のミューテックス借用の問題を避ける方法

use std::sync::Mutex; 
use std::collections::BTreeMap; 

pub struct A { 
    map: BTreeMap<String, String>, 
    mutex: Mutex<()>, 
} 

impl A { 
    pub fn new() -> A { 
     A { 
      map: BTreeMap::new(), 
      mutex: Mutex::new(()), 
     } 
    } 
} 

impl A { 
    fn synchronized_call(&mut self) { 
     let mutex_guard_res = self.mutex.try_lock(); 
     if mutex_guard_res.is_err() { 
      return 
     } 
     let mut _mutex_guard = mutex_guard_res.unwrap(); // safe because of check above 
     let mut lambda = |text: String| { 
      let _ = self.map.insert("hello".to_owned(), 
            "d".to_owned()); 
     }; 
     lambda("dd".to_owned()); 
    } 
}  

エラーメッセージを:私はMutexPlayground)を使用してこれをやってみたかった私たちは私たちが借りまで他の構造体のフィールドを使用することができない構造体から何かを借りるときに私は理解したよう

error[E0500]: closure requires unique access to `self` but `self.mutex` is already borrowed 
    --> <anon>:23:26 
    | 
18 |   let mutex_guard_res = self.mutex.try_lock(); 
    |        ---------- borrow occurs here 
... 
23 |   let mut lambda = |text: String| { 
    |       ^^^^^^^^^^^^^^ closure construction occurs here 
24 |    if let Some(m) = self.map.get(&text) { 
    |        ---- borrow occurs due to use of `self` in closure 
... 
31 |  } 
    |  - borrow ends here 

終了です。しかし、私はどのようにしてメソッド同期を行うことができますか?

+0

を、お願いします?それはたくさんの助けになります! –

+0

@ LukasKalbertodt私はすでにそれをやっています:) –

+0

ありがとうたくさん!私はあなたの答えを少し編集しました、私はあなたが気にしないことを願っています。私はこの形で答える方が簡単だと思います。 –

答えて

5

クロージャーには、何かを挿入するためにself.mapへの変更可能な参照が必要です。しかし、クロージャのキャプチャはバインド全体でのみ機能します。つまり、self.mapと言う場合、クロージャーはではなくselfをキャプチャしようとします。 selfの部分はすでに不本意に借用されているため、selfは変更できません。

我々は(Playground)クロージャがそれをキャプチャすることができるような単独のマップのための新たな結合を導入することにより、この閉鎖捕捉問題を解決することができます:

let mm = &mut self.map; 
let mut lambda = |text: String| { 
    let _ = mm.insert("hello".to_owned(), text); 
}; 
lambda("dd".to_owned()); 

しかしがあり、あなたが見落としたもの:synchronized_call()&mut selfを受け入れるので、あなたはミューテックスを必要としません!どうして? 可変参照は、専用参照とも呼ばれます。コンパイラは、コンパイル時に常にそのような可変参照が1つしかないことを保証できるためです。

したがって、あなたが静的にある特定のオブジェクト上で実行されているsynchronized_call()の1つのインスタンスは、任意の時点で、せいぜいがあること、を知ってもし関数が(自分自身を呼び出す)再帰的ではありません。

mutexへのアクセス権が変更可能な場合は、mutexがロックされていないことがわかります。 the Mutex::get_mut() method for more explanationを参照してください。驚くべきことではないですか?

4

錆びたミューテックスは、使用しようとしている方法では機能しません。 Rustでは、mutexは言語のどこかで使用されているBorrow-Checkメカニズムに依存する特定のデータを保護します。結果として、フィールドMutex<()>を宣言することは、変更する値を持たないユニットオブジェクト()への読み取り/書き込みアクセスを保護しているため意味がありません。その署名がすでに同じオブジェクト上で複数のスレッドから呼び出されてからそれを防ぐselfへの排他的(変更可能)を参照し、要求しているため、宣言として

ルーカスは説明したように、あなたのcall_synchronizedは、同期化を行う必要はありません。つまり、の署名call_synchronizedに変更する必要があります。これは、現在の機能が提供する機能と一致しないためです。

call_synchronizedは、selfへの共有参照を受け入れる必要があります。これは、最初に複数のスレッドから呼び出せることをRustに通知します。call_synchronized内部Mutex::lockへの呼び出しは、同時にミューテックスをロックし、ロックが参照の期間に開催されるように慎重にスコープ基礎となるデータへの変更可能な参照を提供します:あなたは[MCVE]を提供することができ

use std::sync::Mutex; 
use std::collections::BTreeMap; 

pub struct A { 
    synced_map: Mutex<BTreeMap<String, String>>, 
} 

impl A { 
    pub fn new() -> A { 
     A { 
      synced_map: Mutex::new(BTreeMap::new()), 
     } 
    } 
} 

impl A { 
    fn synchronized_call(&self) { 
     let mut map = self.synced_map.lock().unwrap(); 
     // omitting the lambda for brevity, but it would also work 
     // (as long as it refers to map rather than self.map) 
     map.insert("hello".to_owned(), "d".to_owned()); 
    } 
} 
関連する問題