&mut
всегда получает исключительную блокировку (время компиляции или выполнения) к значению. Приобретайте &mut
только в том объеме, который хотите заблокировать. Если значение, которым владеет заблокированное значение, нуждается в отдельном управлении блокировками, оберните его в Mutex
.
Предполагая, что ваша DbConnectionPool
имеет такую структуру:
struct DbConnectionPool {
conns: HashMap<ConnId, Conn>,
}
Нам нужно &mut
HashMap
, когда мы добавляем / удаляем элементы в HashMap
, но нам не нужно &mut
значение в Conn
. Таким образом, Arc
позволяет нам отделить границу изменчивости от ее родителя , а Mutex
позволяет нам добавить собственную внутреннюю изменчивость .
Более того, наш * Метод 1023 * не хочет быть &mut
, поэтому необходимо добавить еще один слой внутренней изменчивости в HashMap
.
Так что мы изменим его на
struct DbConnectionPool {
conns: Mutex<HashMap<ConnId, Arc<Mutex<Conn>>>,
}
Тогда когда вы хотите установить соединение,
fn get(&self, id: ConnId) -> Arc<Mutex<Conn>> {
let mut pool = self.db.conns.lock().unwrap(); // ignore error if another thread panicked
if let Some(conn) = pool.get(id) {
Arc::clone(conn)
} else {
// here we will utilize the interior mutability of `pool`
let arc = Arc::new(Mutex::new(new_conn()));
pool.insert(id, Arc::clone(&arc));
arc
}
}
(параметр ConnId
и логика if-there-else c используются для упрощения кода; вы можете изменить лог c)
На возвращаемое значение вы можете сделать
self.get(id).lock().unwrap().query(...)
. Для удобства я изменил логи c на пользователя, предоставившего идентификатор. На самом деле вы должны быть в состоянии найти Conn
, который не был приобретен, и вернуть его. Затем вы можете вернуть защиту RAII для Conn
, аналогично тому, как работает MutexGuard
, для автоматического освобождения соединения, когда пользователь перестанет его использовать.
Также рассмотрите возможность использования RwLock
вместо Mutex
, если это может привести к повышению производительности.