Я не знаю, какой у вас настоящий код.Но следующий пример воспроизводит точную ошибку:
use std::thread;
use std::sync::{Mutex,Arc};
struct Cursive;
impl Default for Cursive {
fn default() -> Self {
Cursive
}
}
trait View{
fn run(&self);
}
impl View for Cursive{
fn run(&self){}
}
fn main() {
let mut siv:Arc<Mutex<dyn View>> = Arc::new(Mutex::new(Cursive::default()));
let copy_siv = siv.clone();
thread::spawn(move || {
let mut new_siv = copy_siv.lock().unwrap();
});
(*(siv.lock().unwrap())).run();
}
Вы можете попробовать ее на детская площадка .Сообщение об ошибке:
error[E0277]: `dyn View` cannot be sent between threads safely
--> src/main.rs:21:5
|
21 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `dyn View` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `dyn View`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<dyn View>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::Mutex<dyn View>>`
= note: required because it appears within the type `[closure@src/main.rs:21:19: 23:6 copy_siv:std::sync::Arc<std::sync::Mutex<dyn View>>]`
= note: required by `std::thread::spawn`
Анализ и решение
Сообщение об ошибке объяснило все опытным пользователям.Для тех, кто плохо знаком с языком, siv
- это объектный признак с защитой мьютекса.Этот объект известен только как View
, у компилятора нет доказательств того, является ли он Send
.Однако, чтобы код работал,
Arc<Mutex<T>>
должно быть Send
, поскольку вы отправляете такую вещь в другой поток;Следовательно: Mutex<T>
должно быть Send
и Sync
, так как Arc
требует, чтобы объект подсчета ссылок был Send
и Sync
.Поэтому: T
должно быть Send
, так как один и тот же объект будет доступен в разных потоках без какой-либо дополнительной защиты.
Таким образом, этот код не работает.Решение:
let mut siv:Arc<Mutex<dyn View + Send>> = ...
Вы можете попробовать это сами!
Mutex<T>: Send + Sync
требуется T: Send
Чтобы понять почему, сначала задайте вопрос: что не может быть Send
?
Одним из примеров является то, что ссылки на вещи с внутренней изменчивостью не могут быть Send
.Потому что, если бы они были, люди могут мутировать вещь через внутреннюю изменчивость в разных потоках и вызывать гонку данных.
Теперь предположим, что у вас есть Mutex<&Cell<T>>
, потому что защищенная вещь является только ссылкой, а не Cell
Сам по себе Cell
может быть где-то незащищенным.Таким образом, компилятор не может сделать вывод, что при вызове lock().set()
нет риска вызвать гонку данных.Таким образом, компилятор предотвращает его от Send
.
Что если мне нужно ...
Итак, мы видим, что &Cell<T>
не Send
, и поэтому даже он защищен вMutex
мы все еще не можем использовать его в другом потоке.Что мы можем сделать тогда?
Эта проблема на самом деле не нова.Почти все API пользовательского интерфейса имеют одну и ту же проблему: компоненты пользовательского интерфейса были созданы в потоке пользовательского интерфейса, поэтому вы не можете получить к ним доступ ни в каких других потоках.Вместо этого вы должны запланировать выполнение подпрограммы в потоке пользовательского интерфейса и позволить потоку пользовательского интерфейса получить доступ к компоненту.
Не удается сделать это на других языках (.NET, Java ...)исключения в лучшем случае, вызывающие неопределенное поведение в худшем.Еще раз, Rust превращает такие нарушения в ошибки компиляции без специальных обработок (&Cell<T>
не имеет ничего общего с пользовательским интерфейсом), это действительно ХОРОШО!
Итак, если это то, что вы хотели сделать, вы должнысделать то же самое: получить доступ к объекту представления только в потоке пользовательского интерфейса.Как это сделать, зависит от используемого вами API.