Можно ли безопасно использовать многопоточность чего-либо, что не предназначено для многопоточности? - PullRequest
0 голосов
/ 18 октября 2018

Я использую черту, которая не предназначена для многопоточности (Cursive).

Теперь, пока она использует многопоточность, она будет за мьютексом, поэтому она не сможетиспользуется в двух потоках одновременно.

От чего пытается защитить меня ржавчина и могу ли я с этим что-нибудь сделать?

Для примера, мой пример кода:

extern crate cursive;

use cursive::Cursive;
use std::thread;
use std::sync::{Mutex,Arc};

fn main() {
    let mut siv = 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();
 }

Компилятор жалуется на thread::spawn:

   Error[E0277]: `(dyn cursive::traits::View + 'static)` cannot be sent between threads safely
   --> src/main.rs:16:5
   |
16 |     thread::spawn(move || {
   |     ^^^^^^^^^^^^^ `(dyn cursive::traits::View + 'static)` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `(dyn cursive::traits::View + 'static)`

Ответы [ 2 ]

0 голосов
/ 19 октября 2018

Я не знаю, какой у вас настоящий код.Но следующий пример воспроизводит точную ошибку:

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.

0 голосов
/ 18 октября 2018

Что ржавчина пытается защитить меня от [...]

Что-то в том, что вы посылаете между потоками, содержит объект черты dyn cursive::traits::View.Этот объект черты не Send.Это должно быть Send, потому что, помещая его в Arc, вы больше не можете предсказать, какой поток будет отвечать за его уничтожение, поэтому необходимо безопасно передавать права собственности между потоками.

[...] я могу что-нибудь с этим сделать?

Вы не предоставили достаточно контекста, чтобы сказать наверняка, но, вероятно, нет.

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

Почему Mutex не может синхронизировать ее??Разве не в этом смысл Mutex?

Нет.Он не может сделать что-то поточно-ориентированное, если оно не было уже поточно-ориентированным.Mutex просто управляет монопольным доступом к значению, но не делает этот доступ безопасным из разных потоков.Единственное, что может сделать тип потокобезопасным - это рассматриваемый тип.

Делать предположение: библиотека была написана так, что она не требует безопасности потоков, поэтому Arc не может предполагать, что она безопасна для потоковпоэтому он отказывается компилировать.

...