Совместное использование изменяемых переменных между потоками в 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,

Другие вопросы по тегам