parking_lot für Rust: Vollständige Anleitung mit Beispielen

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

parking_lot für Rust: Schnelle Synchronisierungs-Primitive

Die Bibliothek parking_lot ist eine der beliebtesten und leistungsfähigsten Implementierungen von Synchronisierungs-Primitiven für die Programmiersprache Rust. Sie bietet eine Alternative zu den standardmäßigen Mutexen (std::sync::Mutex), Read-Write-Locks (std::sync::RwLock) und Condition-Variablen (std::sync::Condvar), jedoch mit deutlich besserer Performance und geringerem Code-Overhead.

Warum wird diese Bibliothek benötigt? Die standardmäßigen Synchronisierungs-Primitive in Rust sind zwar sicher, haben aber einige Nachteile: Sie verwenden selbst in einfachen Szenarien Systemaufrufe (futex unter Linux), benötigen zusätzlichen Speicher für Flags und haben eine komplexere interne Implementierung. parking_lot löst diese Probleme durch effizientere Algorithmen (z. B. optimistische Sperren und Spin-Loops) und minimiert den Overhead. Das Ergebnis ist schnellerer Code, besonders in Multithread-Anwendungen mit hohem Konkurrenzdruck.

Die Bibliothek ist vollständig kompatibel mit Rust und erfordert keinen unsafe-Code auf der Benutzerseite. Sie bietet dieselben Schnittstellen wie die Standardbibliothek, jedoch mit zusätzlichen Funktionen: Mutex mit Timeout-Unterstützung, RwLock mit fairer Warteschlange, Once für faule Initialisierung und ReentrantMutex für reentrante Sperren. parking_lot ist der De-facto-Standard für leistungsstarke Rust-Anwendungen, von Webservern bis hin zu Spiele-Engines.

Installation

Fügen Sie die Bibliothek in Ihre Cargo.toml-Datei ein:

[dependencies]
parking_lot = "0.12"

Führen Sie dann den folgenden Befehl aus:

cargo build

Oder, wenn Sie cargo add verwenden (ab Rust 1.62):

cargo add parking_lot

Danach können Sie die Primitive aus der Bibliothek importieren:

use parking_lot::{Mutex, RwLock, Condvar, Once, ReentrantMutex};

Schnellstart: Minimales Arbeitsbeispiel

Hier ist ein einfaches Beispiel zur Verwendung von Mutex aus parking_lot, um einen gemeinsamen Zähler in einer Multithread-Umgebung zu schützen:

use parking_lot::Mutex;
use std::thread;

fn main() {
    // Erstellen eines Mutex, der einen Wert umschließt
    let counter = Mutex::new(0);

    // Erstellen von 10 Threads, die den Zähler jeweils um 1 erhöhen
    let mut handles = vec![];
    for _ in 0..10 {
        let handle = thread::spawn(|| {
            // Die Sperre anfordern – .lock() gibt einen RAII-Guard zurück
            let mut guard = counter.lock();
            *guard += 1;
            // Der Guard wird automatisch freigegeben, wenn er den Gültigkeitsbereich verlässt
        });
        handles.push(handle);
    }

    // Warten, bis alle Threads fertig sind
    for handle in handles {
        handle.join().unwrap();
    }

    // Den endgültigen Wert auslesen
    let final_value = *counter.lock();
    println!("Endgültiger Wert: {}", final_value); // Sollte 10 sein
}

In diesem Beispiel gibt counter.lock() einen MutexGuard zurück, der die Sperre beim Verlassen des Gültigkeitsbereichs automatisch freigibt. Im Gegensatz zum standardmäßigen std::sync::Mutex müssen hier keine Fehler (Poisoning) behandelt werden – parking_lot vergiftet den Mutex bei einer Panik standardmäßig nicht.

Wichtige Methoden und Typen

Im Folgenden werden 8 wichtige Methoden/Typen der parking_lot-Bibliothek mit Signaturen, Beschreibungen und Beispielen vorgestellt.

1. Mutex::lock()

Signatur: pub fn lock(&self) -> MutexGuard<'_, T>

Beschreibung: Sperrt den Mutex und blockiert den aktuellen Thread, bis die Sperre erlangt wird. Gibt einen RAII-Guard zurück, der die Sperre beim Verlassen des Gültigkeitsbereichs automatisch freigibt.

use parking_lot::Mutex;

let mutex = Mutex::new(42);
{
    let mut guard = mutex.lock();
    *guard += 1;
} // Sperre wird hier automatisch freigegeben
println!("Wert: {}", *mutex.lock());

2. Mutex::try_lock()

Signatur: pub fn try_lock(&self) -> Option<MutexGuard<'_, T>>

Beschreibung: Versucht, die Sperre zu erlangen, ohne den Thread zu blockieren. Gibt Some(guard) zurück, wenn die Sperre erfolgreich war, andernfalls None.

use parking_lot::Mutex;

let mutex = Mutex::new(10);
if let Some(mut guard) = mutex.try_lock() {
    *guard += 5;
    println!("Sperre erhalten, Wert geändert");
} else {
    println!("Sperre nicht verfügbar");
}

3. RwLock::read()

Signatur: pub fn read(&self) -> RwLockReadGuard<'_, T>

Beschreibung: Erlangt eine Lesesperre. Mehrere Lesesperren können gleichzeitig gehalten werden, solange keine Schreibsperre aktiv ist. Blockiert den Thread, falls eine Schreibsperre aktiv ist.

use parking_lot::RwLock;
use std::thread;

let data = RwLock::new(vec![1, 2, 3]);
let reader1 = thread::spawn(|| {
    let guard = data.read();
    println!("Leser 1: {:?}", *guard);
});
let reader2 = thread::spawn(|| {
    let guard = data.read();
    println!("Leser 2: {:?}", *guard);
});
reader1.join().unwrap();
reader2.join().unwrap();

4. RwLock::write()

Signatur: pub fn write(&self) -> RwLockWriteGuard<'_, T>

Beschreibung: Erlangt eine exklusive Schreibsperre. Blockiert den Thread, bis alle Lesesperren und andere Schreibsperren freigegeben sind.

use parking_lot::RwLock;

let data = RwLock::new(5);
{
    let mut guard = data.write();
    *guard += 10;
} // Schreibsperre wird hier freigegeben
println!("Neuer Wert: {}", *data.read());

5. Condvar::wait()

Signatur: pub fn wait<T>(&self, mutex_guard: &mut MutexGuard<'_, T>)

Beschreibung: Blockiert den aktuellen Thread, bis die Condition-Variable benachrichtigt wird. Die Mutex-Sperre wird während des Wartens freigegeben und nach dem Aufwachen automatisch wieder erlangt.

use parking_lot::{Mutex, Condvar};
use std::thread;

let pair = (Mutex::new(false), Condvar::new());
let (lock, cvar) = &pair;

thread::spawn(move || {
    let mut ready = lock.lock();
    *ready = true;
    cvar.notify_one();
});

let mut ready = lock.lock();
while !*ready {
    cvar.wait(&mut ready);
}
println!("Bereit!");

6. Condvar::notify_one()

Signatur: pub fn notify_one(&self)

Beschreibung: Weckt genau einen wartenden Thread auf, der auf diese Condition-Variable wartet. Falls kein Thread wartet, hat der Aufruf keine Wirkung.

use parking_lot::{Mutex, Condvar};

let pair = (Mutex::new(0), Condvar::new());
let (lock, cvar) = &pair;

// Thread 1 wartet
thread::spawn(move || {
    let mut count = lock.lock();
    while *count < 1 {
        cvar.wait(&mut count);
    }
    println!("Aufgewacht!");
});

// Thread 2 benachrichtigt
thread::spawn(move || {
    let mut count = lock.lock();
    *count += 1;
    cvar.notify_one();
});

7. Once::call_once()

Signatur: pub fn call_once<F>(&self, f: F) where F: FnOnce()

Beschreibung: Führt die übergebene Closure genau einmal aus, unabhängig davon, wie oft call_once aufgerufen wird. Ideal für die faule Initialisierung von globalen Ressourcen.

use parking_lot::Once;
use std::sync::atomic::{AtomicUsize, Ordering};

static INIT: Once = Once::new();
static mut GLOBAL_VALUE: AtomicUsize = AtomicUsize::new(0);

fn get_global() -> usize {
    INIT.call_once(|| {
        unsafe { GLOBAL_VALUE.store(42, Ordering::SeqCst); }
    });
    unsafe { GLOBAL_VALUE.load(Ordering::SeqCst) }
}

fn main() {
    println!("Globaler Wert: {}", get_global());
    println!("Erneuter Aufruf: {}", get_global()); // Führt die Closure nicht erneut aus
}

8. ReentrantMutex::lock()

Signatur: pub fn lock(&self) -> ReentrantMutexGuard<'_, T>

Beschreibung: Erlangt eine reentrante Sperre. Derselbe Thread kann die Sperre mehrfach erlangen, ohne sich selbst zu blockieren. Nützlich für rekursive Aufrufe oder Callbacks.

use parking_lot::ReentrantMutex;

let mutex = ReentrantMutex::new(0);

fn rekursive_funktion(mutex: &ReentrantMutex, tiefe: u32) {
    let mut guard = mutex.lock();
    *guard += 1;
    if tiefe > 0 {
        rekursive_funktion(mutex, tiefe - 1);
    }
}

fn main() {
    rekursive_funktion(&mutex, 5);
    println!("Endgültiger Wert: {}", *mutex.lock());
}

Fazit

parking_lot ist eine unverzichtbare Bibliothek für alle Rust-Entwickler, die Wert auf Leistung und Effizienz legen. Sie bietet eine vollständige und optimierte Alternative zu den Standard-Synchronisierungs-Primitiven und ist dabei einfach zu bedienen. Ob für Webserver, Datenbanken oder Spiele – mit parking_lot holen Sie das Maximum aus Ihrer Multithread-Anwendung heraus.

Probieren Sie es noch heute in Ihrem nächsten Rust-Projekt aus und überzeugen Sie sich selbst von der Leistungssteigerung!

Empfehlungen