Есть ли возможность блокировать несколько мьютексов в Rust, предотвращая блокировку?

Есть ли средство С++ std::lock() в Rust для предотвращения взаимоблокировки в коде следующим образом:

type Type0 = Arc<Mutex<u8>>;
type Type1 = Arc<Mutex<u16>>;

fn foo(a: Type0, b: Type1) {
    let a_guard = a.lock().unwrap();
    let b_guard = b.lock().unwrap();
}

fn bar(a: Type0, b: Type1) {
    let b_guard = b.lock().unwrap();
    let a_guard = a.lock().unwrap();
}

Если foo вызывается потоком-0 и bar потоком-1, существует вероятность блокировки. Есть что-то, надеюсь, вариативно, потому что у меня может быть больше 2, чтобы помочь мне в этом или я все сам по себе проверял правильность порядка блокировки?

Из документация для std::lock:

Заблокирует заданные объекты Lockable lock1, lock2, ..., lockn, используя алгоритм избежания тупиковой ситуации, чтобы избежать тупиковой ситуации.

Ответы

Ответ 1

Нет, у Rust нет (или еще нет) функции, эквивалентной С++ std::lock.

Основываясь на том факте, что он, похоже, не содержится в std::sync документации, а Googling не приносит ничего полезного, я довольно уверен в этом утверждении.

Почему бы и нет? Ну, если я могу немного переработать редакцию, наличие std::lock - не такая уж большая идея - избегание взаимоблокировки является нетривиальным, и каждый алгоритм будет иметь правдоподобные угловые случаи, которые приведут к низкой производительности или даже к оживлению. Существует не один алгоритм избежания взаимозависимости всех случаев.

(Вы можете сделать тот же аргумент для других функций, которые Rust имеет, например, Vec::sort, но я утверждаю, что "сортировка" необходима во многих приложениях, где производительность не является критичной, и любой разумный алгоритм сортировки будет достаточно эффективным для > 75% приложений. Уклонение от тупика в основном необходимо только тогда, когда вы уже пишете код, который должен хорошо работать.)

Кроме того, включение в стандартную библиотеку функции lock, предотвращающей тупик, позволяет предположить, что это хороший выбор по умолчанию и поощряет его использование без учета его реализации. В действительности, большинство приложений, вероятно, лучше справились бы с более простым (и менее универсальным) алгоритмом.

В любом случае, ничто не мешает вам написать собственный алгоритм "отступания" для решения этой проблемы; он просто не входит в стандартную библиотеку.

Ответ 2

Это легко решить, если Mutex является кортежем с содержащимися значениями, так что блокировка кортежа блокирует оба значения одновременно.

let tuple_mutex = Arc::new(Mutex::new((A, B)));