Непонятно, как работает многопоточный код

Не понимаю, почему код ниже компилируется и, вроде, правильно работает.

Что меня смущает: ProjectSet не Send + Sync, соответственно Arc<ProjectSet> тоже будет не Send + Sync. Однако такие ссылки отдаются в несколько потоков. Потенциально запросы к "/api/v1/{}/mr" и "/api/v1/{}/comment" iron может обрабатывать параллельно - требуется Send + Sync на Handler.

Почему доступ вдруг оказывается синхронизированным? Почему молчит компилятор? Тут как-то замешаны замыкания?


#[derive(Debug, Clone)]
struct ProjectSet {
    name: String,
    job_url: String,
    projects: HashMap<i64, Project>,
}

// ...
        let psa = Arc::new(project_set); // project_set: ProjectSet
        let psa2 = psa.clone();
        let psa3 = psa.clone();

        let builder = thread::spawn(move || {
            handle_build_request(&*psa2);
        });
        router.post(format!("/api/v1/{}/mr", psid),
                    move |req: &mut Request|
                    handle_mr(req, &*psa3));
        router.post(format!("/api/v1/{}/comment", psid),
                    move |req: &mut Request|
                    handle_comment(req, &*psa));

Потому что Sync и Send OIBIT-трейты и «просачиваются» на ProjectSet, ведь все его поля Send + Sync. То есть ProjectSet всё-таки Send + Sync.

1 симпатия

Это меня весьма удивляет. Как вдруг получается, что String синхронизирует доступ к своему содержимому? Почему тогда нельзя изменять содержимое строки многопоточно? Т.е. почему не разрешено изменять Arc<String>? Я попробовал так.

Что именно удивляет? String состоит из набора ссылок, которые Sync+Send (потому что по сути числа), и длин (capacity и length), которые тоже Send+Sync. Соответственно String тоже Send+Sync.

А то, что твой пример не собирается, не имеет никакого отношения к Send или Sync, там борроу чекер ругается.

Ты не путай мутабельность и Send/Sync. Send только означает, что значение безопасно передавать (читай «перемещать») в другой поток, Sync — что его можно безопасно читать с разных потоков. Про мутабельность никакой речи не идёт, это ортогональное понятие.

Arc не реализует DerefMut, поэтому ты не можешь позаимствовать мутабельную ссылку из него. То, что Arc: Sync не значит, что он должен давать писать в своё содержимое. Он просто гарантирует, что читать своё содержимое из разных потоков безопасно. Для управления мутабельностью есть Mutex, который (в рантайме) гарантирует единственность мутабельной ссылки.

P.S. вот так работает: http://is.gd/S8ytv6
Обрати внимание на отсутствие mut на String и Arc. Тут мутабельность не нужна, потому что ей управляет Mutex в рантайме, а не компилятор в компайлтайме.

Числа по сути, в моём понимании, не должны были бы быть Sync. Синхронизированный доступ к числу дают атомики, String сделан не на них.

В общем да, я ожидал что Sync означает “безопасно писать из нескольких потоков”, но сейчас я понимаю что это глупость. Всё опирается на эксклюзивность ссылки с правом изменения.

Да, спасибо, я так код и писал “чтобы работало” - не понимал просто суть Sync :smile: