Пытаюсь освоить многопоточность в Rust. Прошу ответить на несколько вопросов. Читаю перевод Rustbook.
Мы оборачиваем данные в sync::Mutex, когда хотим использовать эти данные (эту переменную) в других потоках, чтобы не было "гонки данных"? То есть оборачиваем в Mutex, а там уже когда используем, то lock() и все остальные "становятся в очередь"?
Я сейчас о примере с Rustbook:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(vec![1u32, 2, 3]));
for i in 0..3 {
let data = data.clone();
thread::spawn(move || {
let mut data = data.lock().unwrap();
data[i] += 1;
});
}
thread::sleep_ms(50);
}
Какие-то странные и непонятные действия. Ну, хорошо, связали data с вектором, который обернут в Mutex, но зачем ещё в Arc? Внутри цикла зачем-то делаем копию и связываем с data, потом уже в замыкании опять новая переменная data.
Объясните пожалуйста.
Моя попытка сделать десять потоков и в каждом из них прибавлять по единице к data. Неработающая попытка:
use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let mut data = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..10).map(|_| {
thread::spawn(move|| {
data.lock();
data += 1; //binary assignment operation `+=` cannot be applied to type `alloc::arc::Arc
for h in handles {
h.join();
}
}
Почему опять ему (компилятору) не нравится?
Ответ
В книге очень подробно разобран этот пример с вариантами что будет без мьютексов и что будет без Arc:
Arc
Единственное что там вводит в заблуждение - используется затенение переменных, так что там три разных переменных с одинаковым именем data
Вот рабочий код вашего примера:
use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let data = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..10).map(|_| {
let xd = data.clone();// (1)
thread::spawn(move|| {
let mut x = xd.lock().unwrap();// (2)
*x+=1;// (3)
})
}).collect();
for h in handles {
h.join();
}
println!("{:?}",data)
}
По пунктам что было не так:
Вы пытаетесь непосредственно обратиться к переменной data. Так как для типа Arc не реализован типаж неявного копирования Copy, то первый же поток захватит право владения переменной и она будет недоступна для остальных потоков (и для основного тоже). Поэтому используется явное клонирование указателя data.clone()
Функция lock() возвращает специальный объект MutexGuard
Комментариев нет:
Отправить комментарий