Что означает документация parking_lot, когда говорится «разрешить необработанную блокировку без объекта RAII guard»?

#rust #mutex #raii

#Ржавчина #мьютекс #raii

Вопрос:

В документах для parking_lot говорится:

  1. Mutex и RwLock разрешить необработанную разблокировку без объекта RAII guard.
  2. Mutex<()> и RwLock<()> разрешить необработанную блокировку без объекта RAII guard.

Больше никаких упоминаний об этих функциях, что они означают и как их использовать. Каковы некоторые указатели или примеры использования?

Комментарии:

1. Предположительно, это относится к unsafe raw_unlock и safe raw_lock , последний доступен только на Mutex<()> .

Ответ №1:

Mutex API контролирует доступ к своим данным через guard, который разблокирует его, Mutex когда он выходит за пределы области видимости. Mutex Владеет своими данными и может обеспечить, чтобы они были доступны только через MutexGuard , когда они заблокированы. Оба std::sync::Mutex и parking_lot::Mutex одинаковы в этом отношении.

Однако parking_lot::Mutex также предоставляет доступ к своим внутренним элементам, которые представляют собой необработанный указатель на данные и RawMutex . RawMutex Это просто блокировка, которая не контролирует доступ к данным, а просто отслеживает состояние блокировки.

Одной из причин использования RawMutex может быть использование, когда очень неудобно сохранять MutexGuard в области видимости, и вы готовы управлять статусом блокировки самостоятельно. Это, скорее всего, относится к библиотеке, которая определяет новые примитивы синхронизации или интеллектуальные указатели, а не к коду приложения, но вам также может оказаться полезным, если вы механически переводите существующий код C / C в Rust.

В качестве простого примера, эти функции выполняют то же самое, что и друг с другом, но одна использует unsafe RawMutex :

 use parking_lot::{Mutex, lock_api::RawMutex as _};

fn update_mutex(mutex: amp;Mutex<i32>) {
    let mut guard = mutex.lock();
    *guard = 2;
    // guard goes out of scope here, causing the Mutex to be unlocked
}

fn update_mutex_raw(mutex: amp;Mutex<i32>) {
    let raw_mutex = unsafe { mutex.raw() };
    let data = mutex.data_ptr();
    raw_mutex.lock();
    unsafe { 
        *data = 2;
        // need to manually unlock the RawMutex
        raw_mutex.unlock();  
    };
}
  

RawMutex.unlock() небезопасно, потому что вызов этого, когда murex не заблокирован, вызовет неопределенное поведение. Разыменование указателя данных дважды одновременно также было бы неопределенным поведением, поэтому вам решать убедиться, что вы этого не сделаете, соблюдая состояние блокировки RawMutex .

Как всегда, при использовании unsafe функций внимательно прочитайте документацию и убедитесь, что вы полностью понимаете инварианты, которые вы должны сохранить, чтобы избежать неопределенного поведения.

Ответ №2:

Rust гарантирует, что безопасный код не будет содержать скачки данных (одновременный изменяемый доступ к одному и тому же объекту). Мьютексы позволяют потокам иметь взаимоисключающий доступ к чтению / записи объекта, тем самым избегая гонки.

В других языках (на ум приходят Java и C ) мьютексы явно не связаны с данными. Программисты должны убедиться, что они блокируют и разблокируют их надлежащим образом, получая доступ к данным только в пределах критической секции между блокировкой и разблокировкой. В Rust это означало бы, что безопасный код мог содержать скачки данных, если что-то было написано неправильно.

Решением этого является RAII guard. Мьютекс «владеет» связанным объектом и разрешает доступ на чтение / запись только через RAII guard, который представляет собой блокировку мьютекса. Это MutexGuard тип, возвращаемый std Mutex::lock()

Parking_lot утверждает, что разрешает блокировку / разблокировку без создания RAII guard, что может быть полезно при написании небезопасного кода, выполняющего причудливые вещи с мьютексами из соображений скорости. Это отличает ее от std sync::Mutex , который не предоставляет эти методы.

Основное ограничение RAII guard заключается в том, что по природе RAII они действуют только до тех пор, пока охватываемая область. Кроме того, они содержат ссылку на мьютекс, поэтому «сохранить» «блокировку» за пределами ее области действия сложно из-за правил заимствования Rust.

Методы parking_lot, на которые ссылаются, являются небезопасными raw_unlock и безопасными raw_lock . Поскольку для завершения критической секции raw_lock() требуется связанный raw_unlock() , использование этих функций означает погружение в небезопасный код, что обычно опрометчиво и не нужно, если у вас нет веских оснований полагать, что это единственный способ выполнить то, что вам нужно.

Комментарии:

1. Я был бы очень удивлен, если бы была какая-либо разница в производительности между parking_lot Mutex и RawMutex .

2. Согласен. Это привело бы не к разнице в производительности отдельных операций, а к блокировке и разблокировке в шаблонах, несовместимых с единственным RAII guard, следовательно, уменьшая количество блокировок и разблокировок в целом или сохраняя какое-либо другое свойство.