0Pricing
Learn Rust Coding · Aula

Unindo threads e reunindo resultados

Aguarde as threads e reúna a saída.

Unindo threads e reunindo resultados é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 4 aulas no total.

Por que fazer join das threads?

Criar uma thread inicia o trabalho, mas normalmente você precisa do resultado ou, pelo menos, da confirmação de que ele terminou. Fazer join é a forma de esperar.

Nesta lição, você collect handles, fará join deles e reunirá os valores produzidos em uma resposta final.

join retorna um resultado

join retorna um Result. Em caso de sucesso, ele contém o valor retornado pela closure; se uma thread entrar em panic, ele conterá um Err.

Chamar unwrap fornece o valor diretamente, mas propaga um panic se a thread falhar.

use std::thread;

fn main() {
    let h = thread::spawn(|| 7 * 6);
    let value: i32 = h.join().unwrap();
    println!("answer = {}", value);
}

Coletando os handles primeiro

Para executar threads em paralelo, crie todas elas com spawn antes de fazer qualquer join. Adicione cada identificador a um vetor à medida que avança.

Se fizer join dentro do laço de spawn, cada thread terminará antes que a próxima comece, e a concorrência será perdida.

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

Coletando resultados em um Vec

Você pode fazer join de cada identificador e adicionar os valores retornados a um vetor de resultados.

A ordem de criação e a ordem de união percorrem de zero a três neste caso, portanto o vetor de resultados termina em uma ordem previsível.

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

Somando resultados paralelos

Depois de coletar os resultados, você pode reduzi-los. Aqui, cada thread calcula um valor parcial, e main soma esses valores.

Esse formato de mapear e depois reduzir é um padrão paralelo comum.

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

Dividindo o trabalho em partes

Para dados maiores, divida-os em partes e entregue cada parte à sua própria thread. Cada thread retorna uma soma parcial.

Clonar cada parte em um Vec com propriedade própria permite que a thread seja proprietária da sua fatia com segurança.

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

Lidando com o pânico de uma thread

Se uma thread de trabalho puder entrar em pânico, não use unwrap cegamente. Faça uma correspondência sobre o Result retornado por join para lidar com o caminho de erro.

Assim, a thread principal continua ativa mesmo quando uma thread de trabalho falha.

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"),
    }
}

Coletando em um Vec compartilhado

Em vez de retornar valores, as threads podem adicioná-los a um Arc<Mutex<Vec>> compartilhado. A ordem será, então, não determinística.

Depois de fazer join de todas as threads, bloqueie o vetor uma única vez para ler cada resultado coletado.

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

Threads com escopo

thread::scope permite que as threads tomem dados locais emprestados sem move ou Arc. O escopo garante que todas as threads terminem antes de retornar.

Isso torna as leituras paralelas de dados da pilha práticas e seguras.

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

Retornando resultados do escopo

As threads com escopo também retornam valores por meio dos seus identificadores. Você faz join delas dentro do escopo para reunir os resultados.

Como o escopo bloqueia até a conclusão, os dados emprestados permanecem válidos durante todo o processo.

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

Não se esqueça de fazer join

Se você descartar um JoinHandle sem fazer join, a thread será desanexada. Ela continuará executando, mas você perderá o resultado e qualquer forma de esperar por ela.

O processo também poderá terminar antes que uma thread desanexada conclua. Faça join quando precisar do resultado ou da conclusão.

Verificação rápida

Teste seu entendimento sobre fazer join e coletar resultados.

Recapitulação

Você aprendeu que join retorna um Result contendo o valor ou o pânico de uma thread. Crie todas as threads primeiro e só depois faça join para manter a concorrência.

Você pode coletar resultados em um Vec, reduzi-los ou adicioná-los a um Arc<Mutex<Vec>> compartilhado. Threads com escopo tomam dados locais emprestados com segurança, e descartar um identificador desanexa a thread.

Agora você tem as ferramentas fundamentais para trabalhar com threads e estado compartilhado em Rust.

Perguntas Frequentes

A aula “Unindo threads e reunindo resultados” é grátis?

Sim — o texto completo de “Unindo threads e reunindo resultados” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 4 aulas no total.

O que vou aprender em “Unindo threads e reunindo resultados”?

Aguarde as threads e reúna a saída. Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Learn Rust Coding?

Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Unindo threads e reunindo resultados”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Learn Rust Coding?

Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Criando threads
  2. Movendo dados para threads
  3. Compartilhando com Arc e Mutex
  4. Unindo threads e reunindo resultados
← Voltar para Learn Rust Coding