Nebenläufigkeit und Multithreading in Rust: Sicherheit und Leistung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten
```html

Nebenläufigkeit und Multithreading in Rust: sicheren und schnellen Code schreiben



Moderne Programme arbeiten selten im Einzelthread-Modus. Die Verarbeitung von Anfragen, die Arbeit mit Dateien, Netzwerkoperationen – all das erfordert die parallele Ausführung von Aufgaben. Rust bietet einen einzigartigen Ansatz für Nebenläufigkeit: Es garantiert Speichersicherheit zur Kompilierzeit und verhindert Datenrennen. In diesem Artikel werden wir uns ansehen, wie man mit Threads, Kanälen, Mutexen und Asynchronität in Rust arbeitet.



1. Grundlagen des Multithreadings: Erstellung und Verwaltung von Threads



In Rust werden Threads über die Standardbibliothek std::thread erstellt. Jeder Thread wird unabhängig ausgeführt, aber der Compiler überwacht streng die Lebensdauer von Variablen.



Erstellen eines Threads

use std::thread;use std::time::Duration;

fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("Thread: {}", i); thread::sleep(Duration::from_millis(100)); } });

for i in 1..5 { println!("Hauptthread: {}", i); thread::sleep(Duration::from_millis(100)); }

handle.join().unwrap(); // Warten auf das Ende des Threads}


Besitzübernahme in Closures

Wenn ein Thread Variablen aus dem äußeren Bereich verwendet, muss der Besitz durch move übergeben werden:

let data = vec![1, 2, 3];

let handle = thread::spawn(move || { println!("Daten: {:?}", data);});

handle.join().unwrap();// println!("{:?}", data); // Fehler! data wurde verschoben


2. Datenaustausch: Mutexe und Sperren



Für den sicheren Zugriff auf gemeinsame Daten aus mehreren Threads verwendet Rust Mutex in Verbindung mit Arc (atomarer Referenzzähler).



Beispiel mit Mutex und Arc

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

fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![];

for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); }

for handle in handles { handle.join().unwrap(); }

println!("Ergebnis: {}", *counter.lock().unwrap()); // 10}


Wichtig: Mutex wird automatisch freigegeben, wenn der Gültigkeitsbereich verlassen wird. Dies verhindert Deadlocks auf Compiler-Ebene.



RwLock – für häufiges Lesen

Wenn Daten selten geändert, aber häufig gelesen werden, verwenden Sie RwLock:

use std::sync::RwLock;

let lock = RwLock::new(5);

{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); // Gleichzeitiges Lesen möglich println!("{} {}", r1, r2);}

{ let mut w = lock.write().unwrap(); *w += 1; // Schreiben blockiert Lesen}


3. Kanäle: Nachrichtenaustausch zwischen Threads



Rust unterstützt das Akteurmodell über Kanäle (std::sync::mpsc). Dies ermöglicht Threads den Datenaustausch ohne gemeinsamen Speicher.



Beispiel mit einem Kanal

use std::sync::mpsc;use std::thread;

fn main() { let (tx, rx) = mpsc::channel();

thread::spawn(move || { let vals = vec!["hello", "from", "the", "thread"]; for val in vals { tx.send(val).unwrap(); thread::sleep(std::time::Duration::from_secs(1)); } });

for received in rx { println!("Empfangen: {}", received); }}


Mehrere Sender

Mit mpsc::Sender kann der Sender für mehrere Threads geklont werden:

let (tx, rx) = mpsc::channel();let tx1 = tx.clone();

thread::spawn(move || { tx1.send("Thread 1").unwrap(); });thread::spawn(move || { tx.send("```

Blogs

Buchempfehlungen