我使用的特征不是围绕多线程(草书)设计的。
现在,当它使用多线程时,它将位于互斥锁的后面,因此它将无法同时在两个线程中使用。
什么是铁锈试图保护我免受伤害,我能对此采取任何措施吗?
作为示例参考,我的示例代码为:
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)`
答案 0 :(得分:3)
什么是铁锈试图保护我免受[...]
线程之间要发送的内容中包含dyn cursive::traits::View
特征对象。此特征对象不是Send
。它必须为Send
,因为通过将其放入Arc
中,您将无法再预测哪个线程将负责销毁它,因此在线程之间转移所有权必须是安全的。
[...]我能做些什么吗?
您尚未提供足够的上下文可以肯定地说,但可能没有。
您可以也许尝试使用普通借用的引用(加上支持作用域线程的线程库),但我不能说这是否对您有用。
为什么Mutex不能使其同步?这不是Mutex的重点吗?
不。当它还不是线程安全的时,它就不能使线程安全。 Mutex
仅管理对值的独占访问,并不能保证从不同线程进行的访问都是安全的。唯一可以使类型成为线程安全的是相关类型。
猜测一下:库的编写不需要任何线程安全性,因此Arc
无法假定它是线程安全的,因此它拒绝编译。
答案 1 :(得分:0)
我不知道您的实际代码是什么。但是以下示例复制了您遇到的确切错误:
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();
}
您可以在playground中进行尝试。错误消息:
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
中受到保护,我们仍然不能在其他线程中使用它。那我们该怎么办?
这个问题实际上并不新鲜。几乎所有UI API都有相同的问题:UI组件是在UI线程中创建的,因此您无法在任何其他线程中访问它们。相反,您必须安排要在UI线程中运行的例程,并让UI线程访问组件。
在其他语言(.NET,Java ...)中未这样做的情况将以最佳方式抛出异常,从而在最严重的情况下导致未定义的行为。再一次,Rust将这种违规行为变成没有特殊处理的编译错误(&Cell<T>
与UI无关),这真的很好!
因此,如果这是您要执行的操作,则必须执行相同的操作:仅在UI线程中访问视图对象。具体操作取决于您使用的API。