Concorrência sem Receios com Threads
Aprofunde-se nas primitivas do Rust para programação concorrente, incluindo threads e passagem de mensagens, garantindo a ausência de condições de corrida nos dados.
Concorrência sem Receios com Threads é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 3 de 3. 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 3 aulas no total.
Introdução à concorrência
Boas-vindas à Concorrência sem medo com threads! Nesta lição, vamos aprender a fazer seus programas Rust executarem várias tarefas ao mesmo tempo.
Concorrência significa executar vários cálculos aparentemente ao mesmo tempo. Ela é essencial para aplicações responsivas e para aproveitar processadores modernos com vários núcleos.
- Threads são unidades leves de execução dentro de um programa.
- Cada thread pode executar uma parte separada do seu código.
- O sistema de propriedade do Rust ajuda a evitar erros comuns de concorrência.
Criando sua primeira thread
O Rust fornece std::thread::spawn para criar novas threads. Você passa a ele um fechamento (uma função anônima) que contém o código que a nova thread deve executar.
Experimente executar este exemplo para ver as threads em ação:
use std::thread;
use std::time::Duration;
fn main() {
println!("Hello from the main thread!");
thread::spawn(|| {
for i in 1..=5 {
println!("Hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..=3 {
println!("Hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}Esperando pelas threads com `join`
No exemplo anterior, a thread criada pode não terminar antes que a thread principal seja encerrada. Isso acontece porque, por padrão, a thread principal não espera as threads criadas.
Para garantir que uma thread criada conclua seu trabalho, usamos o método join() no JoinHandle. Isso bloqueia a thread atual até que a thread aguardada termine.
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("Thread: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..=3 {
println!("Main: {}", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap(); // Wait for the spawned thread to finish
println!("Spawned thread has finished!");
}Movendo dados para dentro das threads
Ao usar variáveis do ambiente dentro de um fechamento spawn, as regras de propriedade do Rust entram em ação. Por padrão, os fechamentos tentam pegar as variáveis emprestadas.
Se a thread criada continuar existindo além do escopo da thread principal em que a variável foi definida, isso poderá resultar em uma referência pendente. Para corrigir isso, use a palavra-chave move antes dos parâmetros do fechamento.
moveforça o fechamento a assumir a propriedade das variáveis capturadas.- Isso garante que os dados sejam válidos durante todo o tempo de vida da nova thread.
use std::thread;
fn main() {
let data = String::from("Hello from outer scope");
let handle = thread::spawn(move || { // Use 'move' to take ownership of 'data'
println!("Data in thread: {}", data);
});
handle.join().unwrap();
// println!("Data after thread: {}", data); // This would cause a compile error!
println!("Main thread finished.");
}Comunicação por Passagem de Mensagens
Embora seja possível compartilhar dados diretamente entre threads (estado compartilhado) no Rust, isso exige uma sincronização cuidadosa (por exemplo, com Mutex e Arc, abordados em outras lições).
Uma abordagem mais segura e geralmente mais simples para a concorrência é a passagem de mensagens. As threads se comunicam enviando mensagens umas às outras, evitando o acesso direto à memória compartilhada.
- Uma thread envia dados.
- Outra thread recebe os dados.
- A biblioteca padrão do Rust fornece
std::sync::mpscpara isso.
Configurando um Canal `mpsc`
std::sync::mpsc significa Multiple Producer, Single Consumer (vários produtores, um único consumidor). Isso significa que muitas threads podem enviar mensagens, mas apenas uma thread pode recebê-las.
Para criar um canal, chame mpsc::channel(). Ele retorna uma tupla contendo:
- Um remetente (
tx): usado para enviar mensagens. - Um receptor (
rx): usado para receber mensagens.
Veja como criar um canal e enviar uma mensagem simples.
use std::sync::mpsc;
use std::thread;
fn main() {
// Create a new channel
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap(); // Send the message
// println!("val is {}", val); // Error: val moved to tx.send()
});
// Receive the message in the main thread
let received = rx.recv().unwrap();
println!("Got: {}", received);
}Enviando Várias Mensagens
Você pode enviar várias mensagens pelo mesmo canal. O receptor tem métodos para lidar com isso:
rx.recv(): bloqueia a thread atual até que uma mensagem seja recebida. Retorna umResult.rx.try_recv(): não bloqueia. Retorna umResultimediatamente, contendo uma mensagem ou um erro caso nenhuma mensagem esteja disponível.- Você também pode iterar diretamente sobre o
receptorpara obter mensagens até que o remetente seja fechado.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let msgs = vec!["hi", "from", "the", "thread"];
for msg in msgs {
tx.send(String::from(msg)).unwrap();
thread::sleep(Duration::from_millis(10));
}
});
// Iterate over the receiver to get all messages
for received in rx {
println!("Got: {}", received);
}
println!("All messages received!");
}Vários Produtores, um Único Consumidor
O 'MP' em mpsc significa Multiple Producers (vários produtores). Você pode clonar um remetente para permitir que várias threads enviem mensagens ao mesmo receptor.
Cada remetente clonado compartilha o acesso ao mesmo canal, permitindo o envio concorrente de mensagens a partir de threads diferentes.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone(); // Clone the sender for another thread
thread::spawn(move || {
tx1.send(String::from("hello from tx1")).unwrap();
});
thread::spawn(move || {
tx.send(String::from("hello from tx2")).unwrap();
});
// Collect all messages
let mut received_messages: Vec<String> = vec![];
for received in rx {
received_messages.push(received);
if received_messages.len() == 2 { break; } // Assuming 2 messages for this example
}
println!("Received: {:?}", received_messages);
}Concorrência sem Medo com Rust
O sistema de propriedade e de tipos do Rust é essencial para alcançar uma concorrência sem medo. Ao impor regras em tempo de compilação, o Rust evita erros comuns de concorrência, como condições de corrida de dados.
- A propriedade impede várias referências mutáveis aos mesmos dados.
- A palavra-chave
movegarante que os dados sejam transferidos com segurança para uma nova thread. - A passagem de mensagens (
mpsc) elimina completamente a necessidade de memória compartilhada, tornando a comunicação segura por projeto.
Esses mecanismos permitem escrever código concorrente com confiança, sabendo que o compilador detectará muitos problemas em potencial.
Verificação de Conceitos de Concorrência
Quais das afirmações a seguir sobre as primitivas de concorrência do Rust são TRUE?
Recapitulação: Threads e Passagem de Mensagens
Parabéns! Você aprendeu os fundamentos da concorrência sem medo no Rust:
- Criamos novas threads usando
std::thread::spawn. - Usamos
JoinHandle::join()para esperar a conclusão de uma thread. - A palavra-chave
movetransfere com segurança a propriedade dos dados para uma thread. std::sync::mpscfornece um sistema robusto de passagem de mensagens para a comunicação entre threads.- As regras de propriedade do Rust evitam condições de corrida de dados em tempo de compilação, tornando a programação concorrente mais segura.
Esta lição fornece uma base sólida para criar aplicações responsivas e eficientes. Continue praticando!
Perguntas Frequentes
A aula “Concorrência sem Receios com Threads” é grátis?
Sim — o texto completo de “Concorrência sem Receios com Threads” é 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 3 aulas no total.
O que vou aprender em “Concorrência sem Receios com Threads”?
Aprofunde-se nas primitivas do Rust para programação concorrente, incluindo threads e passagem de mensagens, garantindo a ausência de condições de corrida nos dados. 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 3 de 3.
Quanto tempo leva a aula “Concorrência sem Receios com Threads”?
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
- Ponteiros Inteligentes Box, Rc e Arc
- Mutabilidade Interior: RefCell e Cell
- Concorrência sem Receios com Threads