Lær at programmere i Rust · Lektion

Arbejde med futures og tasks

Forstå traitet `Future`, og lær, hvordan tasks planlægges og håndteres i et asynkront runtime-miljø.

Lektion 3 af 311 trin

Arbejde med futures og tasks er en gratis Lær at programmere i Rust-lektion på CoddyKit. Dette er lektion 3 af 3. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Lær at programmere i Rust, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Lær at programmere i Rust-kurset indeholder 3 lektioner i alt.

Hvad er en Rust Future?

I asynkron Rust er en Future et trait, der repræsenterer en asynkron beregning, som kan blive færdig på et tidspunkt. Tænk på den som et løfte om en værdi, der endnu ikke er klar.

  • Den er den grundlæggende byggesten i asynkron Rust.
  • async fn-funktioner i Rust returnerer faktisk en anonym type, der implementerer traitet Future.
  • Værdien beregnes ikke med det samme; den beregnes, når en eksekveringsmotor udfører "poll" på Future-objektet.

Sådan gør Futures fremskridt

En Future kører ikke af sig selv. En eksekveringsmotor (f.eks. Tokio-runtime-miljøet) udfører gentagne gange "poll" på den for at kontrollere, om den har gjort fremskridt eller er blevet færdig.

  • Når der udføres poll, returnerer en Future enten Poll::Pending (ikke færdig endnu) eller Poll::Ready(T) (færdig, her er resultatet).
  • Hvis resultatet er Pending, ved eksekveringsmotoren, at den skal udføre poll igen senere, når der sker noget relevant (f.eks. når I/O er færdig).
  • Denne poll-mekanisme gør det muligt for mange asynkrone operationer at køre samtidigt på en enkelt tråd.

Din asynkrone kode er en Future

Når du skriver en async fn, omdanner Rust den til en tilstandsmaskine, der implementerer traitet Future. Den faktiske beregning starter først, når der udføres poll på den returnerede Future.

Lad os se på en enkel async-funktion. Den kører ikke, før den bliver afventet af en eksekveringsmotor.

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 bliver til opgaver

Mens en Future er definitionen på en asynkron beregning, er en opgave en aktiv instans af denne Future, som en eksekveringsmotor driver frem til afslutning.

  • Når du beder en eksekveringsmotor (f.eks. Tokio) om at køre en Future, pakker den den ind i en opgave.
  • Eksekveringsmotoren administrerer derefter denne opgave og udfører poll på den, hver gang den er klar til at gøre fremskridt.
  • Opgaver er de arbejdsenheder, som det asynkrone runtime-miljø planlægger og udfører samtidigt.

Opret opgaver for samtidighed

Hvis du vil køre en Future samtidigt med anden kode, skal du "oprette" den i Tokio-runtime-miljøet. Det opretter en ny opgave, som runtime-miljøet administrerer.

Funktionen tokio::spawn tager en Future og returnerer en JoinHandle, som du kan bruge til at afvente, at opgaven bliver færdig, og hente dens resultat.

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

Hent resultater fra opgaver

Den JoinHandle, som tokio::spawn returnerer, er selv en Future. Du kan .await dette handle for at vente på, at den nye opgave bliver færdig, og hente dens returværdi.

Hvis den nye opgave udløser en panic, returnerer det en fejl, når du afventer dens JoinHandle.

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.");
}

Flere samtidige opgaver

Opgaver og tokio::spawn viser for alvor deres styrke, når du kører flere operationer samtidigt. Tokio-runtime-miljøet skifter effektivt mellem opgaverne, efterhånden som de bliver klar.

Det gør det muligt for dit program at gøre fremskridt med mange ting på én gang uden at blokere, selv på en enkelt tråd.

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.");
}

Vent på alle med join!

Når du har brug for at vente på, at flere Future-objekter bliver færdige samtidig, er tokio::join! en nyttig makro. Den venter på alle de angivne futures samtidigt og returnerer deres resultater som en tupel.

Den minder om at afvente hvert handle individuelt, men er ofte mere kompakt for et fast antal futures.

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.");
}

Håndtering af fejl i opgaver

Asynkrone operationer kan mislykkes, ligesom synkrone operationer. Det er almindeligt, at Futures returnerer typen Result, som angiver, om operationen lykkedes eller mislykkedes.

Når du afventer en JoinHandle, skal du huske, at resultatet er pakket ind i endnu en Result, fordi selve opgaven kan udløse en panic. Du vil ofte se handle.await? (hvis den er i en async fn, der returnerer Result) eller 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(())
}

Forstå Futures og opgaver

Du har lært om Futures som beregninger og opgaver som deres udførende instanser. Hvilken påstand om tokio::spawn og JoinHandle er sand?

Opsummering: Futures og opgaver

Godt gået! Du har nu fået styr på de centrale begreber Futures og opgaver i Rusts asynkrone økosystem:

  • En Future er et trait, der repræsenterer en asynkron beregning, som på et tidspunkt vil producere en værdi.
  • async fn-funktioner kompileres til typer, der implementerer traitet Future.
  • En opgave er en instans af en Future, som en eksekveringsmotor (f.eks. Tokio) aktivt administrerer og udfører poll på.
  • tokio::spawn bruges til at oprette en ny opgave og returnerer en JoinHandle.
  • Du afventer en JoinHandle med .await for at få resultatet af en ny opgave.

Disse byggesten er afgørende, når du skal skrive effektive, ikke-blokerende Rust-programmer!

Gratis at komme i gang

Lær Rust med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
39
Lektioner
144

Ofte stillede spørgsmål

Er lektionen “Arbejde med futures og tasks” gratis?

Ja — alle 3 lektioner i læringssporet Lær at programmere i Rust, inklusive “Arbejde med futures og tasks”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Lær at programmere i Rust-kurset indeholder 3 lektioner i alt.

Hvad lærer jeg i “Arbejde med futures og tasks”?

Forstå traitet `Future`, og lær, hvordan tasks planlægges og håndteres i et asynkront runtime-miljø. Du øver dig i Lær at programmere i Rust med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Lær at programmere i Rust?

Der kræves ingen tidligere erfaring. Lær at programmere i Rust på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 3.

Hvor lang tid tager lektionen “Arbejde med futures og tasks”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Lær at programmere i Rust-lektion?

Ja. Alle Lær at programmere i Rust-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Introduktion til async/await
  2. Opbygning af asynkrone applikationer med Tokio
  3. Arbejde med futures og tasks
← Tilbage til Lær at programmere i Rust