Совместное использование изменяемых переменных между потоками в Rust
Можно ли разделить изменяемую переменную между несколькими потоками в Rust? Учитывая следующее:
fn main() {
let mut msg = "Hi";
// ...
msg = "Hello, World!";
do spawn {
println(msg);
}
do spawn {
println(msg);
}
}
Я получаю эту ошибку:
Переменная должна быть только для чтения порожденным потокам. Хотя переменная должна быть изменчивой, потому что я действительно пытаюсь разделить HashMap между несколькими потоками. Насколько я знаю, нет способа заполнить HashMap, если он не изменчив. Даже если есть способ сделать это, мне все равно интересно знать, как сделать что-то подобное в целом.
Спасибо!
3 ответа
Это ограничение планируется снять в будущей версии языка. Тем не менее, вы можете решить эту проблему с let msg = msg;
до первого do spawn
, Это переместит значение msg
в неизменном месте, эффективно изменяя его изменчивость.
"Общий" ответ (о том, как сохранить хеш-таблицу непостоянной, пока она совместно используется) заключается в том, что это невозможно напрямую; Rust был специально разработан, чтобы запретить совместное использование изменяемого состояния, чтобы избежать определенных типов ошибок и защитить определенные виды безопасности (и по другим причинам).
Тем не менее, можно создать что-то похожее на общую изменяемую хеш-таблицу. Представьте, что у одной задачи есть изменяемая хеш-таблица. Вы можете написать код, чтобы другие задачи могли отправлять ему сообщения со словами: "Обновите хеш-таблицу, чтобы 5 соответствовали 18" или "Скажите, что 7 отображает".
Что делает эта косвенность? Во-первых, копируя значения в сообщения, невозможно для другой задачи растоптать всю память, которая находится в процессе чтения (большая проблема безопасности), и, во-вторых, косвенно записывая код, это становится более понятным для программиста. что является и не является атомарной операцией; программист будет знать, что не следует ожидать, что два последовательных сообщения о содержимом хеш-таблицы не обязательно относятся к одной и той же версии хеш-таблицы; предположение, что оно совершенно верно (по крайней мере в Rust, благодаря некоторым другим ограничениям), о двух последовательных чтениях из неразделенного, изменчивого, хеш-таблицы.
Если ваша переменная может быть атомарной, вы можете использовать Arc
для подсчета ссылок и атомарного типа. AnArc
это счетчик ссылок, который действует подобно сборщику мусора в языках, в которых есть:
use std::sync::atomic;
use std::sync::Arc;
fn main() {
let arc_num = Arc::new(atomic::AtomicU32::new(1337));
let arc_num_clone = Arc::clone(&arc_num);
let _get_borrowed_clone = move || {
return arc_num_clone.load(atomic::Ordering::Relaxed);
};
arc_num.store(4242, atomic::Ordering::Relaxed)
}
Скорее всего, вы не можете использовать атомарный тип, и в этом случае вы можете использовать Arc
а также Mutex
:
use std::sync::{Arc, Mutex};
fn main() {
let arc_num = Arc::new(Mutex::new(1337));
let arc_num_clone = Arc::clone(&arc_num);
let _get_borrowed_clone = move || {
let out = *arc_num_clone.lock().unwrap();
return out;
};
*arc_num.lock().unwrap() = 4242;
}
Подробнее о том, как работает этот код
Если вы хотите узнать больше о разнице между этими двумя подходами, прочтите " Есть ли разница между" мьютексом "и" атомарной операцией "?
В случае совместного использования строки "Hello, World!" Вам просто нужно переместить переменную обратно в неизменяемое место (например, "let mut msg = .....; let msg = msg;").
Возможно, вы также хотите избежать создания отдельной копии хэш-карты для каждого потока, который его получает, но в этом случае вы также захотите поместить его в ARC
(оболочка с атомным подсчетом ссылок), которую вы найдете в extra::arc
,