Mit Futures und Tasks arbeiten
Verstehen Sie das `Future`-Trait und wie Tasks innerhalb einer asynchronen Laufzeitumgebung geplant und verwaltet werden.
Mit Futures und Tasks arbeiten ist eine kostenlose Learn Rust Coding-Lektion auf CoddyKit. Dies ist Lektion 3 von 3. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Learn Rust Coding-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Learn Rust Coding-Kurs umfasst insgesamt 3 Lektionen.
Was ist ein Rust-Future?
In asynchronem Rust ist ein Future ein Trait, das eine asynchrone Berechnung repräsentiert, die irgendwann abgeschlossen werden kann. Stellen Sie es sich als Versprechen für einen Wert vor, der noch nicht bereit ist.
- Es ist ein zentraler Baustein für asynchrones Rust.
async fns geben in Rust tatsächlich einen anonymen Typ zurück, der dasFuture-Trait implementiert.- Der Wert wird nicht sofort berechnet, sondern erst, wenn ein Executor das
Future„pollt“.
Wie Futures Fortschritt machen
Ein Future wird nicht von selbst ausgeführt. Ein Executor (z. B. die Tokio-Runtime) „pollt“ es wiederholt, um zu prüfen, ob es Fortschritte gemacht hat oder abgeschlossen ist.
- Beim Polling gibt ein
FutureentwederPoll::Pending(noch nicht abgeschlossen) oderPoll::Ready(T)(abgeschlossen, hier ist das Ergebnis) zurück. - Bei
Pendingweiß der Executor, dass er später erneut pollen soll, sobald etwas Relevantes passiert (z. B. eine E/A-Operation abgeschlossen ist). - Dieser Polling-Mechanismus ermöglicht die nebenläufige Ausführung vieler asynchroner Vorgänge auf einem einzigen Thread.
Ihr asynchroner Code ist ein Future
Wenn Sie eine async fn schreiben, wandelt Rust sie in eine Zustandsmaschine um, die das Future-Trait implementiert. Die eigentliche Berechnung beginnt erst, wenn das zurückgegebene Future gepollt wird.
Sehen wir uns eine einfache async-Funktion an. Sie wird erst ausgeführt, wenn ein Executor auf sie wartet.
async fn say_hello() -> String {
"Hello from a Future!".to_string()
}
#[tokio::main]
async fn main() {
let future = say_hello(); // This doesn't run the function yet!
println!("Future created, but not awaited.");
// To run it, an executor needs to poll it, often via .await
// let result = future.await;
// println!("{}", result);
}Futures werden zu Aufgaben
Während ein Future die Definition einer asynchronen Berechnung ist, ist eine Aufgabe eine aktive Instanz dieses Future, die von einem Executor bis zum Abschluss ausgeführt wird.
- Wenn Sie einen Executor (z. B. Tokio) anweisen, ein
Futureauszuführen, verpackt er es in eine Aufgabe. - Der Executor verwaltet diese Aufgabe und pollt sie, sobald sie bereit ist, Fortschritte zu machen.
- Aufgaben sind die Arbeitseinheiten, die die asynchrone Runtime plant und nebenläufig ausführt.
Aufgaben für Nebenläufigkeit starten
Um ein Future nebenläufig mit anderem Code auszuführen, „starten“ Sie es in der Tokio-Runtime. Dadurch wird eine neue Aufgabe erstellt, die von der Runtime verwaltet wird.
Die Funktion tokio::spawn nimmt ein Future entgegen und gibt ein JoinHandle zurück. Damit können Sie auf den Abschluss der Aufgabe warten und ihr Ergebnis abrufen.
async fn perform_task(id: u8) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
format!("Task {} finished!", id)
}
#[tokio::main]
async fn main() {
println!("Main started.");
let handle = tokio::spawn(async {
perform_task(1).await
});
println!("Task 1 spawned.");
// We will await 'handle' in the next scene to get the result.
// let result = handle.await.unwrap();
// println!("{}", result);
}Ergebnisse von Aufgaben abrufen
Das von tokio::spawn zurückgegebene JoinHandle ist selbst ein Future. Sie können mit .await auf dieses Handle warten, bis die gestartete Aufgabe abgeschlossen ist, und ihren Rückgabewert abrufen.
Wenn die gestartete Aufgabe in Panik gerät, gibt das Warten auf ihr JoinHandle einen Fehler zurück.
async fn perform_task(id: u8) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
format!("Task {} finished!", id)
}
#[tokio::main]
async fn main() {
println!("Main started.");
let handle = tokio::spawn(async {
perform_task(1).await
});
println!("Task 1 spawned.");
let result = handle.await.unwrap(); // Await the handle to get the result
println!("{}", result);
println!("Main finished.");
}Mehrere nebenläufige Aufgaben
Die Leistungsfähigkeit von Aufgaben und tokio::spawn zeigt sich besonders deutlich, wenn Sie mehrere Vorgänge nebenläufig ausführen. Die Tokio-Runtime wechselt effizient zwischen den Aufgaben, sobald diese bereit sind.
Dadurch kann Ihr Programm gleichzeitig an vielen Dingen Fortschritte machen, ohne blockiert zu werden – sogar auf einem einzigen Thread.
async fn long_task(id: u8) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(100 + id as u64 * 50)).await;
format!("Long task {} done!", id)
}
#[tokio::main]
async fn main() {
println!("Main started.");
let handle1 = tokio::spawn(long_task(1));
let handle2 = tokio::spawn(long_task(2));
println!("Both tasks spawned.");
let result1 = handle1.await.unwrap();
let result2 = handle2.await.unwrap();
println!("{}\n{}", result1, result2);
println!("Main finished.");
}Mit join! auf alle warten
Wenn Sie gleichzeitig auf den Abschluss mehrerer Futures warten müssen, ist tokio::join! ein nützliches Makro. Es wartet nebenläufig auf alle angegebenen Futures und gibt ihre Ergebnisse als Tupel zurück.
Es ähnelt dem individuellen Warten auf jedes Handle, ist bei einer festen Anzahl von Futures jedoch häufig kürzer.
async fn fetch_data(source: &str) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
format!("Data from {}", source)
}
#[tokio::main]
async fn main() {
println!("Starting data fetches...");
let (data_a, data_b) = tokio::join!(
fetch_data("Server A"),
fetch_data("Database B")
);
println!("Fetched: {}\nFetched: {}", data_a, data_b);
println!("All fetches complete.");
}Fehler in Aufgaben behandeln
Asynchrone Vorgänge können ebenso wie synchrone fehlschlagen. Häufig geben Futures einen Result-Typ zurück, der Erfolg oder Fehlschlag anzeigt.
Beachten Sie beim Warten auf ein JoinHandle, dass das Ergebnis in ein weiteres Result verpackt ist, da die Aufgabe selbst in Panik geraten kann. Häufig sehen Sie handle.await? (innerhalb einer async fn, die Result zurückgibt) oder handle.await.unwrap() / .expect().
async fn might_fail(should_fail: bool) -> Result<String, &'static str> {
tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
if should_fail {
Err("Oops, something went wrong!")
} else {
Ok("Operation successful!".to_string())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let handle_ok = tokio::spawn(might_fail(false));
let handle_err = tokio::spawn(might_fail(true));
let result_ok = handle_ok.await??; // Await handle, then await inner Result
println!("{}", result_ok);
match handle_err.await? { // Await handle, then handle inner Result
Ok(val) => println!("{}", val),
Err(e) => eprintln!("Error: {}", e),
}
Ok(())
}Futures und Aufgaben verstehen
Sie haben Futures als Berechnungen und Tasks als deren Ausführungsinstanzen kennengelernt. Welche Aussage über tokio::spawn und JoinHandle ist richtig?
Rückblick: Futures und Aufgaben
Großartige Arbeit! Sie beherrschen nun die grundlegenden Konzepte von Futures und Aufgaben im asynchronen Rust-Ökosystem:
- Ein
Futureist ein Trait, das eine asynchrone Berechnung repräsentiert, die schließlich einen Wert liefert. async fns werden in Typen kompiliert, die dasFuture-Trait implementieren.- Eine Aufgabe ist eine Instanz eines
Future, die von einem Executor (z. B. Tokio) aktiv verwaltet und gepollt wird. tokio::spawnwird zum Erstellen einer neuen Aufgabe verwendet und gibt einJoinHandlezurück.- Mit
.awaitauf einemJoinHandlerufen Sie das Ergebnis einer gestarteten Aufgabe ab.
Diese Bausteine sind unverzichtbar, um effiziente, nicht blockierende Rust-Anwendungen zu schreiben!
Häufig gestellte Fragen
Ist die Lektion „Mit Futures und Tasks arbeiten“ kostenlos?
Ja — der vollständige Text von „Mit Futures und Tasks arbeiten“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Learn Rust Coding-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Learn Rust Coding-Kurs umfasst insgesamt 3 Lektionen.
Was lerne ich in „Mit Futures und Tasks arbeiten“?
Verstehen Sie das `Future`-Trait und wie Tasks innerhalb einer asynchronen Laufzeitumgebung geplant und verwaltet werden. Du übst Learn Rust Coding mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Learn Rust Coding zu starten?
Keine Vorkenntnisse erforderlich. Learn Rust Coding auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 3.
Wie lange dauert die Lektion „Mit Futures und Tasks arbeiten“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Learn Rust Coding-Lektion Code schreiben und ausführen?
Ja. Jede Learn Rust Coding-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Einführung in Async/Await
- Asynchrone Anwendungen mit Tokio entwickeln
- Mit Futures und Tasks arbeiten