<spanclass="caption">Листинг 16-12: Изучение API <code>Mutex<T></code> для простоты в однопоточном контексте</span>
Как и во многих типах, мы создаём `Mutex<T>`используя ассоциированную функцию`new`. Чтобы получить доступ к данным внутри мьютекса, мы используем метод `lock` для получения блокировки. Этот вызов блокирует текущий поток, поэтому он не может выполнять какую-либо другую работу, пока не наступит наша очередь получить блокировку.
Как и во многих других типах, мы создаём `Mutex<T>`с помощью сопутствующей функции`new`. Чтобы получить доступ к данным внутри мьютекса, мы используем метод `lock` для получения блокировки. Этот вызов блокирует выполнение текущего потока, так что он не сможет выполнять никакие действия, до тех пор пока не наступит наша очередь получить блокировку.
Вызов `lock`завершится неудачей, если запаникует другой поток, удерживающий блокировку. В этом случае никто никогда не сможет получить блокировку, поэтому мы решили вызвать `unwrap` и вызвать панику, если окажемся в такой ситуации.
Вызов `lock`потерпит неудачу, если другой поток, удерживающий блокировку, запаникует. В таком случае никто не сможет получить блокировку, поэтому мы предпочли использовать `unwrap` и заставить этот поток паниковать, если мы окажемся в такой ситуации.
После того как мы получили блокировку, мы можем рассматривать возвращаемое значение, в данном случае с именем `num`, как изменяемую ссылку на данные внутри. Система типов гарантирует, что мы получим блокировку перед использованием значения из `m`. Переменная `m` имеет тип`Mutex<i32>`, а не просто `i32`, поэтому мы *должны* вызвать функцию`lock`, чтобы иметь возможность использовать значение типа <code>i32</code>. Мы не можем забыть об этом; без этого система типов не позволит нам получить доступ ко внутреннему `i32` значению.
После получения блокировки мы можем воспринимать возвращённое значение, названное в данном случае `num`, как изменяемую ссылку на содержащиеся внутри данные. Система типов гарантирует, что мы получим блокировку перед использованием значения в `m`. Тип `m` -`Mutex<i32>`, а не `i32`, поэтому мы *должны* вызвать `lock`, чтобы иметь возможность использовать значение <code>i32</code>. Мы не должны об этом забывать, тем более что в иных случаях система типов и не даст нам доступ к внутреннему значению `i32`.
Как вы наверное подозреваете, `Mutex<T>` является умным указателем. Точнее, вызов `lock`*возвращает* умный указатель, называемый `MutexGuard`, обёрнутый в `LockResult`, который мы обработали с помощью вызова `unwrap`. Умный указатель типа `MutexGuard` реализует типаж `Deref` для указания на внутренние данные; умный указатель также имеет реализацию типажа `Drop`, автоматически снимающего блокировку, когда `MutexGuard` выходит из области видимости, что происходит в конце внутренней области видимости. В результате у нас нет риска забыть снять блокировку и оставить мьютекс в заблокированном состоянии, препятствуя его использованию другими потоками (снятие блокировки происходит автоматически).
Ничего себе, это сообщение об ошибке очень многословно! Вот важная часть, на которой следует сосредоточиться: ``Rc<Mutex<i32>>` cannot be sent between threads safely`. Компилятор также сообщает нам причину: `the trait `Send` is not implemented for `Rc<Mutex<i32>>``. Мы поговорим о `Send` в следующем разделе: это один из типажей, который гарантирует, что типы которые мы используем с потоками, предназначены для использования в в многопоточном коде.
Ничего себе, это сообщение об ошибке очень многословно! Вот важная часть, на которой следует сосредоточиться: `Rc<Mutex<i32>>` cannot be sent between threads safely`. Компилятор также сообщает нам причину: `the trait `Send` is not implemented for `Rc<Mutex<i32>>`. Мы поговорим о `Send` в следующем разделе: это один из типажей, который гарантирует, что типы которые мы используем с потоками, предназначены для использования в в многопоточном коде.
К сожалению, `Rc<T>` небезопасен для совместного использования между потоками. Когда `Rc<T>` управляет счётчиком ссылок, он добавляется значение к счётчику для каждого вызова `clone` и вычитается значение из счётчика, когда каждое клонированное значение удаляется при выходе из области видимости. Но он не использует примитивы многопоточности, чтобы гарантировать, что изменения в подсчёте не могут быть прерваны другим потоком. Это может привести к неправильным подсчётам - незначительным ошибкам, которые в свою очередь, могут привести к утечкам памяти или удалению значения до того, как мы отработали с ним. Нам нужен тип точно такой же как `Rc<T>`, но который позволяет изменять счётчик ссылок безопасно из разных потоков.
Мы сделали это! Мы посчитали от 0 до 10, что может показаться не очень впечатляющим, но это позволило больше узнать про `Mutex<T>` и безопасность потоков. Вы также можете использовать структуру этой программы для выполнения более сложных операций, чем просто увеличение счётчика. Используя эту стратегию, вы можете разделить вычисления на независимые части, разделить эти части на потоки, а затем использовать `Mutex<T>`, чтобы каждый поток обновлял конечный результат своей частью кода.
Обратите внимание, что если вы выполняете простые числовые операции, существуют более простые типы, чем `Mutex<T>` , предоставляемые [модулем `std::sync::atomic` стандартной библиотеки] . Эти типы обеспечивают безопасный многопоточный атомарный доступ для примитивных типов. В этом примере мы решили использовать `Mutex<T>` с примитивным типом, чтобы сосредоточиться на том, как работает `Mutex<T>`.
Обратите внимание, что если вы выполняете простые числовые операции, то существуют типы более простые, чем `Mutex<T>`, которые предоставляет модуль [`std::sync::atomic` стандартной библиотеки]<!-- ignore -->. Эти типы обеспечивают безопасный, параллельный, атомарный доступ к простым типам. Мы решили использовать `Mutex<T>` с простым типом в этом примере, чтобы подробнее рассмотреть, как работает `Mutex<T>`.
### Сходства `RefCell<T>` / `Rc<T>` и `Mutex<T>` / `Arc<T>`