Canais
A passagem de mensagens permite que threads se comuniquem enviando dados de propriedade através de canais em vez de compartilhar estado mutável. A biblioteca padrão do Rust fornece mpsc; o ecossistema adiciona filas com limite, select e mais.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(42).unwrap();
});
println!("{}", rx.recv().unwrap());
}Quando usar isso: Quando trabalhadores produzem resultados, estágios de pipeline passam trabalho, ou você quer evitar locks e mutação compartilhada.
Exemplo de Trabalho
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let producer = thread::spawn(move || {
for i in 0..5 {
tx.send(i).unwrap();
thread::sleep(Duration::from_millis(10));
}
});
for msg in rx {
println!("recebido {msg}");
}
producer.join().unwrap();
}O que isso demonstra:
mpsc::channelcria uma fila ilimitada (a memória pode crescer).sendmove a propriedade; o remetente pode ser clonado para múltiplos produtores.- Iterar sobre
rxbloqueia até que os remetentes sejam descartados e a fila esteja vazia.
Mergulho Profundo
Como Funciona
- Transferência de propriedade: Cada mensagem é movida, não copiada (a menos que
Copy). Nenhum lock no corpo da mensagem após o envio. - Consumidor único:
mpscpadrão tem um único receptor. Múltiplos receptores precisam decrossbeam-channelou padrões de fan-out. - Backpressure: Canais ilimitados nunca bloqueiam no envio; canais com limite bloqueiam quando cheios.
- Desconexão:
recvretornaErrquando todos os remetentes são descartados.
std::sync::mpsc vs crossbeam
| Recurso | std::sync::mpsc | crossbeam-channel |
|---|---|---|
| Fila com limite | não | sim |
| Múltiplos consumidores | não | sim |
select! / try_recv em lote | limitado | sim |
| Padrão do ecossistema | apenas std | comum em produção |
Notas de Rust
// Múltiplos produtores: clone o remetente
let (tx, rx) = mpsc::channel();
let tx2 = tx.clone();
thread::spawn(move || { tx.send(1).unwrap(); });
thread::spawn(move || { tx2.send(2).unwrap(); });
drop(tx); // descarta o original para que o iterador rx possa terminar- Clone
Senderantes de mover para cada thread. - Descarte todos os remetentes quando terminar para que
recvpossa retornarDisconnected. - Prefira
crossbeam_channel::bounded(n)quando os produtores podem superar os consumidores.
Armadilhas
- Crescimento ilimitado de memória - Produtores rápidos com consumidores lentos enchem a RAM. Correção: Use um canal com limite ou aplique backpressure.
- Esquecer de descartar remetentes -
recvbloqueia para sempre se um clone ainda existir. Correção: Rastreie os handles dos remetentes e descarte os extras explicitamente. - Enviar tipos não
Send- Mensagens de canal devem serSend. Correção: UseArcou reestruture; não envieRcentre threads. - Mutex dentro de cada mensagem - Anula o propósito da passagem de mensagens. Correção: Envie dados de propriedade ou IDs, não locks compartilhados.
- Panicking no
sendapós desconexão - O receptor foi embora significa quesendfalha. Correção: TrateErrou uselet _ = tx.send(...)com logging.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Arc<Mutex<T>> | Estado compartilhado com atualizações infrequentes | Muitos produtores escrevendo mensagens independentes |
crossbeam-channel com limite | Pipelines de produção com backpressure | Você só precisa do std e ilimitado está bom |
Tokio mpsc | Serviços assíncronos | Código de thread puramente síncrono |
| Buffer circular de memória compartilhada | Latência ultra baixa, capacidade fixa | Você precisa de dimensionamento dinâmico ou muitos consumidores |
FAQs
Devo usar canais std ou crossbeam?
Comece com std::sync::mpsc para aprendizado e ferramentas simples. Use crossbeam-channel quando precisar de filas com limite, múltiplos consumidores ou select.
Posso compartilhar o receptor entre threads?
Não com mpsc std - apenas um Receiver. Divida o trabalho no lado do remetente ou use padrões multi-consumidor do crossbeam.
O que acontece se o receptor for descartado?
send retorna Err(SendError(msg)) e você recupera a propriedade da mensagem.
Canais são mais rápidos que mutexes?
Para mensagens independentes, canais evitam contenção de lock. Para contadores compartilhados "quentes", atômicos ou um único mutex podem ser mais simples.
Como sinalizo o desligamento?
Envie uma mensagem sentinela, use um flag bool em um Arc<AtomicBool>, ou descarte os remetentes e deixe recv retornar Disconnected.
Canais podem carregar `Result`?
Sim. Muitos pipelines enviam Result<T, E> para que os trabalhadores relatem falhas por item.
sync_channel vs channel?
sync_channel(n) bloqueia após n mensagens pendentes - backpressure embutido.
Como isso se relaciona com os canais do Go?
Filosofia semelhante (compartilhar comunicando). Canais Rust movem propriedade e são tipados; backpressure com limite é explícito.
Canais Async vs sync?
Não bloqueie um executor async em std::sync::mpsc::recv. Use tokio::sync::mpsc em código async.
Testando código de canal?
Use timeouts curtos (recv_timeout) ou canais com limite em testes para evitar travamentos quando um bug deixa um remetente ativo.
Relacionados
- Noções Básicas de Concorrência - primeiro exemplo de canal
- Mutex & RwLock - alternativa de estado compartilhado
- Deadlocks & Condições de Corrida - menos corridas com mensagens
- Canais em Tokio - canais assíncronos
- Melhores Práticas de Concorrência - prefira passagem de mensagens
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.