Как поделиться общей структурой между потоками с помощью Arc ‹Mutex‹ MyStruct ‹T› ››?

У меня есть изменяемое состояние, которое мне нужно передать между потоками. Я следил за разделом параллелизма книги Rust, в котором используется общий вектор между потоками и мутирует его.

Вместо вектора мне нужно поделиться общей структурой, которая в конечном итоге мономорфизируется. Вот обобщенный пример того, что я пытаюсь:

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use std::marker::PhantomData;

trait Memory {}

struct SimpleMemory;

impl Memory for SimpleMemory {}

struct SharedData<M: Memory> {
    value: usize,
    phantom: PhantomData<M>,
}

impl<M: Memory> SharedData<M> {
    fn new() -> Self {
        SharedData {
            value: 0,
            phantom: PhantomData,
        }
    }
}

fn main() {
    share(SimpleMemory);
}

fn share<M: Memory>(memory: M) {
    let data = Arc::new(Mutex::new(SharedData::<M>::new()));

    for i in 0..3 {
        let data = data.clone();
        thread::spawn(move || {
            let mut data = data.lock().unwrap();
            data.value += i;
        });
    }

    thread::sleep(Duration::from_millis(50));
}

Компилятор выдает следующую ошибку:

error[E0277]: the trait bound `M: std::marker::Send` is not satisfied
  --> src/main.rs:37:9
   |
37 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^
   |
   = help: consider adding a `where M: std::marker::Send` bound
   = note: required because it appears within the type `std::marker::PhantomData<M>`
   = note: required because it appears within the type `SharedData<M>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<SharedData<M>>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::Mutex<SharedData<M>>>`
   = note: required because it appears within the type `[closure@src/main.rs:37:23: 40:10 data:std::sync::Arc<std::sync::Mutex<SharedData<M>>>, i:usize]`
   = note: required by `std::thread::spawn`

Я пытаюсь понять, почему M необходимо реализовать Send и как это сделать.


person w.brian    schedule 14.10.2016    source источник


Ответы (1)


Я пытаюсь понять, почему M нужно реализовать Send, ...

Потому что, как указано в Send документации:

Типы, которые можно передавать через границы потоков.

Если это не Send, по определению небезопасно отправлять в другой поток.

Практически вся необходимая информация находится прямо здесь в документации:

  • thread::spawn требует, чтобы вызываемый вами объект был Send.
  • Вы используете замыкание, которое равно Send, только если все фиксируемые значения равны Send. Это верно в целом для большинства типов (они Send, если все, из чего они сделаны, Send, и то же самое для Sync).
  • Вы захватываете data, который является Arc<T>, что составляет только Send, если T Send.
  • T - это Mutex<U>, что равно Send, только если U равно Send.
  • U это M. Таким образом, M должно быть Send.

Кроме того, обратите внимание, что thread::spawn также требует, чтобы вызываемый объект был 'static, так что вам это тоже нужно. Ему нужно это, потому что, если бы он этого не требовал, у него не было бы гарантии, что значение будет продолжать существовать в течение всего времени жизни потока (которое может пережить или не пережить поток, который породил Это).

..., и как это сделать.

Так же, как и любые другие ограничения: M: 'static + Send + Memory.

person DK.    schedule 14.10.2016
comment
Сбивающий с толку аспект ограничения Send + 'static состоит в том, что в книге Rust они обертывают Vec, который на первый взгляд не имеет срока жизни 'static, поскольку он определен в методе main. При дальнейшем осмотре это 'static, потому что это буквальное значение. Это не для всех очевидно. Когда я изначально добавил ограничение Send, оно жаловалось, что оно должно быть 'static, оно, казалось, игнорировало то, что передавал пример, отсюда и вопрос. Снисходительный тон вашего ответа неоправдан и плохо отражается на ржавом сообществе. - person w.brian; 14.10.2016
comment
@ w.brian Дело не в буквальном значении, а в том, что он может жить столько, сколько захочет. Другими словами, в самом типе нет ничего, что ограничивало бы срок его действия (например, ссылка на нестатические данные): ответ на вопрос, когда значение этого типа становится недействительным, никогда не будет. - person DK.; 15.10.2016
comment
@ w.brian Что касается моего тона, боюсь, он у вас в голове: если бы я хотел проявить снисходительность, я бы не стал просматривать и получать ссылки на каждую конкретную страницу документации, чтобы вы могли больше следить за моими рассуждениями без труда. Моресо, я считаю оскорбительным и мелочным с вашей стороны клеветать на все сообщество за воспринимаемый тон одного человека. Независимо от меня, в этом сообществе есть одни из самых отзывчивых, приветливых и трудолюбивых добровольцев, которых я когда-либо видел. Если вы хотите иметь проблемы с кем-то, решайте их с мной. - person DK.; 15.10.2016
comment
Я ценю ответ, но это означает, что я не проявил должной осмотрительности. И нравится вам это или нет, но вы представляете сообщество, когда даете ответы на этот тег. - person w.brian; 15.10.2016