基本多线程:如何在线程移动闭包中使用没有Send特性的结构



我正在学习Rust,并试图制作一个简单的egui GUI应用程序,在一个单独的线程中轮询telnet主机,以避免主线程锁定GUI。我正在为客户端使用telnet机箱。

以下是我遇到问题的代码:

struct TelnetApp {
gui_status: AppView,
telnet_ip: String,
telnet_port: String,
telnet_connect_failed_display: bool,
telnet_connect_failed_message: String,
telnet_client: Arc<Mutex<Option<Telnet>>>,
telnet_result : Arc<Mutex<String>>,    
}
impl TelnetApp {
// Called from gui after successfully connecting to Telnet host
fn start_telnet_loop(&mut self) {
let arc_telnet_result = self.telnet_result.clone();
let arc_telnet_client = self.telnet_client.clone();
let time = SystemTime::now();

thread::spawn(move || { // <---- ERROR: `(dyn Stream + 'static)` cannot be sent between threads safely
loop {
thread::sleep(Duration::from_millis(1000));                
arc_telnet_client.lock().unwrap().unwrap().read(); // <--- This line causes error
{
// I can read and modify the String, presumably because it implements Send?
*arc_telnet_result.lock().unwrap() = String::from(format!("Time {}", time.elapsed().unwrap().as_micros()));                   
}
}
});
}
}

正如我用注释标记的那样,线程生成行给了我一个错误,这似乎源于arc_telnet_client没有实现trait";发送";,当删除线路时错误消失时:

arc_telnet_client.lock().unwrap().unwrap().read()

我读到Arc<Mutex<>>中的包装是处理多线程的推荐方法,但这仍然没有提供发送特性。

为什么我的方法不被允许,即使我使用互斥锁来锁定它?您将如何实现这样一个简单的轮询线程?

为什么我的方法不被允许,即使我使用互斥锁来锁定它?

因为!Send意味着对象移动到其他线程根本不安全。你如何保护它并不重要,它只是无效的。

例如,它可能使用线程本地数据,或内核任务资源,或某种未受保护的全局或共享状态,或仿射机制。无论如何或为什么,如果它是!Send,它只能从创建它的线程访问,也只能由创建它的螺纹访问,而不管你用什么包装它。MutexGuard:就是一个例子

impl<T: ?Sized> !Send for MutexGuard<'_, T>

这是因为互斥体通常只能从锁定它们的线程解锁(在posix中是UB,在windows上发布失败(

正如其特征所描述的,如果对象是Send:,则互斥是Sync(因此可以在线程之间共享(

impl<T: ?Sized + Send> Sync for Mutex<T>

这是因为从语义上讲,在Send周围有一个互斥体相当于通过通道将被包装的对象从一个线程移动到下一个线程(被包装的物体总是一次从一个螺纹使用(。

另一方面,RwLock需要Sync,因为封装的对象可以从不同的线程并发访问(在读取模式下(:

impl<T: ?Sized + Send + Sync> Sync for RwLock<T>

最新更新