Атомарное подсчет ссылок с помощью Arc<T>
{=html}
К счастью, Arc<T>
- это такой же тип, как Rc<T>
, который безопасен для использования в конкурентных ситуациях. Буква a означает атомарность, то есть это атомарно подсчитываемый по ссылкам тип. Атомарные типы - это дополнительный вид примитивов многопоточности, о которых мы здесь не будем подробно останавливаться: для получения более подробной информации обратитесь к документации по стандартной библиотеке для std::sync::atomic
. В этом случае вам просто нужно знать, что атомарные типы работают аналогично примитивным типам, но безопасны для обмена между потоками.
Затем вы, возможно, спросите себя, почему все примитивные типы не являются атомарными и почему типы стандартной библиотеки по умолчанию не реализованы для использования Arc<T>
. Причина заключается в том, что обеспечение безопасности потоков связано с потерей производительности, которую вы хотите платить только тогда, когда это действительно необходимо. Если вы просто выполняете операции с значениями внутри одного потока, ваш код может работать быстрее, если не нужно обеспечивать гарантии, которые предоставляют атомарные типы.
Вернемся к нашему примеру: Arc<T>
и Rc<T>
имеют одинаковый API, поэтому мы исправляем нашу программу, изменив строку use
, вызов new
и вызов clone
. Код в Listing 16-15 наконец-то скомпилируется и запустится.
Filename: src/main.rs
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
Listing 16-15: Использование Arc<T>
для оборачивания Mutex<T>
, чтобы иметь возможность разделить владение между несколькими потоками
Этот код выведет следующее:
Result: 10
Мы сделали это! Мы посчитали от 0 до 10, что, может быть, не кажется очень впечатляющим, но это действительно научило нас много о Mutex<T>
и безопасности потоков. Вы также можете использовать структуру этой программы для выполнения более сложных операций, чем просто увеличение счетчика. Используя эту стратегию, вы можете разделить вычисление на независимые части, разбить эти части между потоками, а затем использовать Mutex<T>
, чтобы каждый поток обновлял окончательный результат своей частью.
Обратите внимание, что если вы выполняете простые числовые операции, существуют типы, проще Mutex<T>
, предоставляемые модулем std::sync::atomic
стандартной библиотеки. Эти типы обеспечивают безопасный, конкурентный, атомарный доступ к примитивным типам. Мы выбрали использовать Mutex<T>
с примитивным типом для этого примера, чтобы сосредоточиться на том, как работает Mutex<T>
.