问题
I'm using a trait which isn't designed around multithreading (Cursive).
Now, while it's using multithreading, it's going to be behind a mutex, so it won't be able to be used at two threads at the same time.
What is rust trying to protect me against and can I do anything about it?
For sample reference, my sample code is:
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();
}
The compiler complains at 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)`
回答1:
What is rust trying to protect me against [...]
Something in what you're sending between threads contains a dyn cursive::traits::View
trait object. This trait object is not Send
. It needs to be Send
because by putting it inside an Arc
, you can no longer predict which thread will be responsible for destroying it, so it must be safe to transfer ownership between threads.
[...] can I do anything about it?
You haven't provided enough context to say for certain, but probably not.
You could maybe try using a plain borrowed reference (plus a threading library that supports scoped threads), but I can't say if that will work for you.
Why wouldn't Mutex make it sync? Isn't that the point of Mutex?
No. It can't make something thread-safe when it wasn't already thread-safe. Mutex
just manages exclusive access to a value, it doesn't make that access from different threads safe. The only thing that can make a type thread-safe is the type in question.
Making a guess: the library was written such that it does not require thread safety, thus Arc
cannot assume it's thread-safe, so it refuses to compile.
回答2:
I don't know what your actual code is. But the following example replicate the exact error you have:
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();
}
You can try it in playground. The error message:
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`
Analysis and Solution
The error message explained everything to experienced users. For those new to the language, siv
is a reference counted, mutex protected trait object. This object only known to be a View
, the compiler have no evidence on whether or not it is Send
. However, for the code to work,
Arc<Mutex<T>>
must beSend
, as you are sending such a thing to another thread; Therefore:Mutex<T>
must beSend
andSync
, asArc
requires the reference counted object to beSend
andSync
. Therefore:T
must beSend
, as the same object will be accessed in different threads without any further protection.
So, this code does not work. The solution is
let mut siv:Arc<Mutex<dyn View + Send>> = ...
You can try it yourself!
Mutex<T>: Send + Sync
requires T: Send
To see why, first ask a question: what cannot be Send
?
One example is that references to things with interior mutablity cannot be Send
. Because if they were, people can mutate the thing through interior mutability in different threads and causes data race.
Now suppose you have a Mutex<&Cell<T>>
, because the protected thing is only a reference, not the Cell
itself, the Cell
itself may still be somewhere unprotected. The compiler thus cannot conclude when you call lock().set()
there is no risk to cause data race. So the compiler prevent it from Send
.
What if I have to ...
So we see that &Cell<T>
is not Send
, and so even it is protected in Mutex
we still cannot use it in another thread. What can we do then?
This problem is actually not new. Almost all UI API have the same problem: the UI components were created in the UI thread, and so you cannot access them in any other threads. Instead, you have to schedule a routine to be run in the UI thread, and let the UI thread to access the component.
Fails to do so in other languages (.NET, Java...) will throw exceptions in the best, causing undefined behavior in the worst. Once again, Rust turns such violates into compile errors without special treatments (&Cell<T>
have nothing to do with UI), this is really GOOD!
So, if this is what you wanted to do, you have to do the same thing: access the view object in the UI thread only. How to do so depends on the API you were using.
来源:https://stackoverflow.com/questions/52866447/can-i-safely-multithread-something-which-isnt-meant-to-be-multithreaded