Как создать изменчивую коллекцию Mutex? - PullRequest
3 голосов
/ 27 февраля 2020

Мне нужна изменчивая коллекция Mutex, которую нужно разделить между несколькими потоками. Цель этой коллекции - вернуть для данного ключа список MutexGuard (чтобы иметь возможность синхронизировать потоки в зависимости от ключа). Обратите внимание, что при инициализации в коллекции нет Mutex, их необходимо создавать во время выполнения в зависимости от ключа.

Мой код следующий:

use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};

struct Bucket {
    locks: HashMap<String, Mutex<()>>,
}

impl Bucket {
    // This method will create and add one or multiple Mutex to the 
    // collection (therefore it needs to take self as mutable), according 
    // to the give key (if the Mutex already exist it will just return
    // its MutexGuard).
    fn get_guards(
        &mut self,
        key: impl Into<String>,
    ) -> Vec<MutexGuard<'_, ()>> {
        let lock = self.locks.entry(key.into()).or_default();
        vec![lock.lock().unwrap()]
    }
}

impl Default for Bucket {
    fn default() -> Self {
        Self {
            locks: HashMap::new(),
        }
    }
}

fn main() {
    // I need to wrap the bucket in a Arc<Mutex> since it's going to be shared
    // between multiple threads
    let mut bucket = Arc::new(Mutex::new(Bucket::default()));

    // Here I need to get a list of guards, so that until they are dropped
    // I can synchronize multiple threads with the same key (or to be more
    // precise with the same MutexGuards, as different keys may yields the
    // same MutexGuards).
    let guards = {
        // IMPORTANT: I need to unlock the Mutex used for the `bucket` (with
        // write access) asap, otherwise it will nullify the purpose of the
        // bucket, since the write lock would be hold for the whole `guards`
        // scope.
        let mut b = bucket.lock().unwrap();
        b.get_guards("key")
    };
}

Ссылка на игровую площадку

Я получаю следующую ошибку:

error[E0597]: `b` does not live long enough
  --> src/main.rs:29:9
   |
27 |     let _guards = {
   |         ------- borrow later stored here
28 |         let mut b = bucket.lock().unwrap();
29 |         b.get_guards("key")
   |         ^ borrowed value does not live long enough
30 |     };
   |     - `b` dropped here while still borrowed

error: aborting due to previous error

Можно ли спроектировать мои Bucket коллекции Mutex так, чтобы я можно достичь моей цели?

1 Ответ

1 голос
/ 28 февраля 2020

По сути, вы хотите позаимствовать объект у заблокированного и оставить его после разблокирования вмещающего его объекта.

Non-Stati c заимствования в этом случае невозможны, потому что это небезопасно, например , любая другая нить может отбросить владельца объекта, который вы ранее заимствовали .

Согласно вашей логике c, внутренняя locks должна быть разделена между другими потоками, чтобы безопасно обернуть mutexes с Arc.

struct Bucket {
    locks: HashMap<String, Arc<Mutex<()>>>,
}

И это возвратит ссылку на атомы c внутренних мьютексов.

//previously get_guards
fn get_mutexes(&mut self, key: impl Into<String>) -> Vec<Arc<Mutex<()>>> {
    let lock = self.locks.entry(key.into()).or_default();
    vec![lock.clone()]
}

Вы можете просто заблокировать все необходимые мьютексы следующим образом:

let mutexes = bucket.lock().unwrap().get_mutexes("key"); // locks(borrows bucket's guard) temporarily in here
let guards: Vec<MutexGuard<'_, ()>> =
    mutexes.iter().map(|guard| guard.lock().unwrap()).collect();

Пожалуйста, посмотрите полный код на детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...