[FIXED] Grundlegendes Multithreading: Verwendung von Struct ohne Send-Merkmal im Thread-Move-Closure

Ausgabe

Ich lerne Rust und versuche, eine einfache Egui- GUI-App zu erstellen, die einen Telnet-Host in einem separaten Thread abfragt, um zu vermeiden, dass der Hauptthread die GUI sperrt. Ich verwende die Telnet -Kiste für den Client.

Hier ist der Code, wo ich Probleme habe:

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()));                   
                }
            }
        });
    }
}

Wie ich mit einem Kommentar markiert habe, gibt mir die Thread-Spawn-Zeile einen Fehler, der darauf zurückzuführen zu sein scheint, dass arc_telnet_client das Trait “Send” nicht implementiert, da der Fehler beim Entfernen der Zeile verschwindet:

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

Ich habe gelesen, dass das Einschließen Arc<Mutex<>>der empfohlene Weg ist, um mit Multithreading umzugehen, aber dies gibt immer noch nicht die Eigenschaft Send.

Warum ist mein Ansatz nicht erlaubt, selbst wenn ich einen Mutex verwende, um ihn zu sperren? Wie würden Sie einen einfachen Umfrage-Thread wie diesen implementieren?

Lösung

Warum ist mein Ansatz nicht erlaubt, selbst wenn ich einen Mutex verwende, um ihn zu sperren?

Weil !Sendbedeutet, dass das Objekt überhaupt nicht sicher in einen anderen Thread verschoben werden kann. Es spielt keine Rolle, wie Sie es schützen, es ist einfach nicht gültig.

Beispielsweise könnte es Thread-lokale Daten oder Kernel-Task-Ressourcen oder eine Art ungeschützten globalen oder gemeinsam genutzten Zustand oder Affinitätsmechanismen verwenden. Egal wie oder warum, wenn es ist !Send, kann nur von und durch den Thread zugegriffen werden, in dem es erstellt wurde, egal in was Sie es einschließen. Ein Beispiel dafür ist MutexGuard:

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

Das liegt daran, dass Mutexe häufig nur von dem Thread entsperrt werden können, der sie gesperrt hat (es ist UB in Posix, die Freigabe schlägt unter Windows fehl).

Wie seine Eigenschaften beschreiben, ist ein Mutex Sync (kann also zwischen Threads geteilt werden), wenn ein Objekt Send:

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

Das liegt daran, dass ein Mutex um einen Send semantisch gleichbedeutend ist mit dem Verschieben des umschlossenen Objekts von einem Thread zum nächsten durch einen Kanal (das umschlossene Objekt wird immer von einem Thread nach dem anderen verwendet).

RwLock hingegen erfordert, Syncda auf das umschlossene Objekt gleichzeitig von verschiedenen Threads (im Lesemodus) zugegriffen werden kann:

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


Beantwortet von –
Masklinn


Antwort geprüft von –
Mildred Charles (FixError Admin)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like