Dołączanie wątków i zbieranie wyników
Poczeka Pan/Pani na wątki i zbierze dane wyjściowe.
Dołączanie wątków i zbieranie wyników to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Po co łączyć wątki?
Utworzenie wątku rozpoczyna pracę, ale zazwyczaj potrzebujesz jej wyniku albo przynajmniej potwierdzenia, że została zakończona. Łączenie wątków pozwala na nie czekać.
W tej lekcji zbierzesz uchwyty, połączysz wątki i zgromadzisz wartości, które zwrócą, aby uzyskać końcowy wynik.
join zwraca Result
join zwraca Result. W przypadku powodzenia zawiera wartość zwróconą przez domknięcie, a jeśli wątek zgłosi panic — zawiera Err.
Wywołanie unwrap zwraca wartość bezpośrednio, ale propaguje panic, jeśli wątek zakończył się niepowodzeniem.
use std::thread;
fn main() {
let h = thread::spawn(|| 7 * 6);
let value: i32 = h.join().unwrap();
println!("answer = {}", value);
}Najpierw zbierz uchwyty
Aby uruchomić wątki równolegle, utwórz je wszystkie przed połączeniem któregokolwiek z nich. W miarę tworzenia wątków dodawaj każdy uchwyt do wektora.
Jeśli połączysz wątek wewnątrz pętli tworzącej wątki, każdy z nich zakończy pracę przed rozpoczęciem następnego, a współbieżność zostanie utracona.
use std::thread;
fn main() {
let handles: Vec<_> = (0..4)
.map(|i| thread::spawn(move || i * i))
.collect();
for h in handles {
println!("{}", h.join().unwrap());
}
}Zbieranie wyników do Vec
Możesz połączyć każdy uchwyt i dodać zwrócone wartości do wektora wyników.
Zarówno kolejność tworzenia, jak i kolejność łączenia przebiega tutaj od zera do trzech, dlatego wektor wyników ma przewidywalną kolejność.
use std::thread;
fn main() {
let handles: Vec<_> = (1..=4)
.map(|n| thread::spawn(move || n * 10))
.collect();
let results: Vec<i32> = handles
.into_iter()
.map(|h| h.join().unwrap())
.collect();
println!("{:?}", results);
}Sumowanie wyników równoległych
Po zebraniu wyników możesz je zredukować. W tym przykładzie każdy wątek oblicza częściową wartość, a funkcja main sumuje je.
Ten schemat mapowania, a następnie redukcji, jest powszechnym wzorcem programowania równoległego.
use std::thread;
fn main() {
let handles: Vec<_> = (1..=5)
.map(|n| thread::spawn(move || n * n))
.collect();
let total: i32 = handles
.into_iter()
.map(|h| h.join().unwrap())
.sum();
println!("total = {}", total);
}Dzielenie pracy na fragmenty
W przypadku większych danych podziel je na fragmenty i przekaż każdy fragment do osobnego wątku. Każdy wątek zwraca sumę częściową.
Sklonowanie każdego fragmentu do posiadanego przez wątek Vec pozwala bezpiecznie przejąć na własność jego wycinek.
use std::thread;
fn main() {
let data: Vec<i32> = (1..=10).collect();
let handles: Vec<_> = data
.chunks(5)
.map(|c| { let c = c.to_vec(); thread::spawn(move || c.iter().sum::<i32>()) })
.collect();
let total: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
println!("{}", total);
}Obsługa paniki wątku
Jeśli wątek roboczy może zgłosić panic, nie używaj bezwarunkowo unwrap. Dopasuj wzorzec do Result zwróconego przez join, aby obsłużyć ścieżkę błędu.
Dzięki temu główny wątek pozostanie aktywny nawet wtedy, gdy jeden z wątków roboczych zakończy się niepowodzeniem.
use std::thread;
fn main() {
let h = thread::spawn(|| {
if true { panic!("failed"); }
1
});
match h.join() {
Ok(v) => println!("got {}", v),
Err(_) => println!("thread panicked"),
}
}Zbieranie danych do współdzielonego Vec
Zamiast zwracać wartości, wątki mogą dodawać je do współdzielonego Arc<Mutex<Vec>>. Kolejność będzie wtedy niedeterministyczna.
Po połączeniu wszystkich wątków zablokuj wektor jeden raz, aby odczytać wszystkie zebrane wyniki.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let out = Arc::new(Mutex::new(vec![]));
let mut handles = vec![];
for i in 0..3 {
let o = Arc::clone(&out);
handles.push(thread::spawn(move || o.lock().unwrap().push(i)));
}
for h in handles { h.join().unwrap(); }
println!("{:?}", out.lock().unwrap());
}Wątki zakresowe
thread::scope pozwala wątkom pożyczać lokalne dane bez używania move ani Arc. Zakres gwarantuje, że wszystkie wątki zakończą pracę przed jego zwróceniem.
Dzięki temu równoległy odczyt danych ze stosu jest wygodny i bezpieczny.
use std::thread;
fn main() {
let data = vec![1, 2, 3];
thread::scope(|s| {
s.spawn(|| println!("sum {}", data.iter().sum::<i32>()));
s.spawn(|| println!("len {}", data.len()));
});
}Zwracanie wyników z zakresu
Wątki zakresowe również zwracają wartości za pośrednictwem swoich uchwytów. Aby zebrać wyniki, połącz je wewnątrz zakresu.
Ponieważ zakres czeka na zakończenie wszystkich wątków, pożyczone dane pozostają ważne przez cały ten czas.
use std::thread;
fn main() {
let nums = vec![4, 5, 6];
let total = thread::scope(|s| {
let h = s.spawn(|| nums.iter().sum::<i32>());
h.join().unwrap()
});
println!("total = {}", total);
}Nie zapomnij połączyć wątków
Jeśli usuniesz JoinHandle bez łączenia wątku, wątek zostanie odłączony. Będzie nadal działać, ale utracisz jego wynik i możliwość oczekiwania na jego zakończenie.
Proces może również zakończyć się, zanim odłączony wątek skończy pracę. Łącz wątek, gdy potrzebujesz jego wyniku lub potwierdzenia zakończenia.
Szybki test
Sprawdź swoją wiedzę o łączeniu wątków i zbieraniu wyników.
Podsumowanie
Dowiedziałeś się, że join zwraca Result zawierający wartość wątku albo informację o panic. Najpierw utwórz wszystkie wątki, a dopiero potem je połącz, aby zachować współbieżność.
Możesz zbierać wyniki do Vec, redukować je albo dodawać do współdzielonego Arc<Mutex<Vec>>. Wątki zakresowe bezpiecznie pożyczają lokalne dane, a usunięcie uchwytu odłącza wątek.
Masz teraz podstawowe narzędzia do pracy z wątkami i współdzielonym stanem w języku Rust.
Często zadawane pytania
Czy lekcja „Dołączanie wątków i zbieranie wyników” jest bezpłatna?
Tak — pełny tekst „Dołączanie wątków i zbieranie wyników” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Co nauczysz się w „Dołączanie wątków i zbieranie wyników”?
Poczeka Pan/Pani na wątki i zbierze dane wyjściowe. Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?
Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Dołączanie wątków i zbieranie wyników”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?
Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Uruchamianie wątków
- Przenoszenie danych do wątków
- Współdzielenie za pomocą Arc i Mutex
- Dołączanie wątków i zbieranie wyników