Combinando Futures
Trabalhos assíncronos independentes podem ser executados concorrentemente com join!, try_join! ou FuturesUnordered. Quando você precisa da primeira conclusão ou cancelamento, use select! ou tokio::select!.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
#[tokio::main]
async fn main() {
let (a, b) = tokio::join!(
async { 1 },
async { 2 },
);
println!("{}", a + b);
}Quando usar isso: Buscas HTTP paralelas, espera em múltiplos canais ou corrida de um timeout contra I/O lento.
Exemplo de Trabalho
use tokio::time::{sleep, Duration, timeout};
async fn slow() -> &'static str {
sleep(Duration::from_secs(5)).await;
"late"
}
#[tokio::main]
async fn main() {
tokio::select! {
result = slow() => println!("got {result}"),
_ = sleep(Duration::from_millis(100)) => println!("timeout branch"),
}
match timeout(Duration::from_millis(50), slow()).await {
Ok(v) => println!("{v}"),
Err(_) => println!("timed out"),
}
}O que isso demonstra:
select!executa até que um branch complete (com viés pela ordem de declaração, a menos quebiased;seja omitido).timeoutenvolve qualquer future com um prazo.- Os branches restantes são cancelados quando
select!resolve.
Mergulho Profundo
Como Funciona
join!: Espera por todos os branches; retorna uma tupla de resultados.try_join!: Interrompe na primeira ocorrência deErr(futures deResult).select!: O primeiro branch vence; os outros são descartados, a menos que seja um loopselect!.FuturesUnordered: Conjunto dinâmico de futures completando na ordem de finalização.
Comparação de Macros
| Ferramenta | Completa quando | Cancelamento |
|---|---|---|
join! | Todos terminados | Descarta incompletos se um entrar em pânico |
select! | Primeiro terminado | Descarta outros branches |
timeout | Future ou prazo | Cancela o future interno no timeout |
join_all | Todos no vec | Coleção de resultados |
Notas de Rust
use futures::future::join_all;
#[tokio::main]
async fn main() {
let futs = vec![async { 1 }, async { 2 }, async { 3 }];
let results = join_all(futs).await;
println!("{:?}", results);
}tokio::select!requer contextoasynce runtime Tokio para branches de timer.- Justiça: use padrões
select!aleatórios ou embaralhe para escolha imparcial em casos raros. - Em loops,
select!é o multiplexador de eventos idiomático.
Armadilhas
select!em um loop apertado sem branches cancel-safe - Pode perder eventos. Correção: Usebiasedcom cuidado; leia a documentação de segurança de cancelamento do Tokio por método.join!com futures dependentes - O segundo precisa da saída do primeiro - não são independentes. Correção:awaitsequencial outhen.- Mutex guard entre branches
select- Não éSend. Correção: Escopo de locks fora deselect!. - Starvation de
selectcom viés - O primeiro branch sempre pronto causa starvation nos outros. Correção: Reordene os branches ou use padrões de polling justos. join_allsem limites - Cria N futures concorrentes de uma vez. Correção:buffer_unorderedou batching com limite de semáforo.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
await Sequencial | Passos dependem da saída anterior | I/O paralelo independente |
spawn + JoinHandle | Tarefas concorrentes reais com tempos de vida | Join de tupla simples em uma tarefa |
Canais + select! | Servidores orientados a eventos | Buscas paralelas únicas |
FuturesOrdered | Precisa de resultados na ordem de submissão | A ordem do primeiro a ficar pronto é suficiente |
FAQs
join! vs spawn?
join! executa futures na tarefa atual concorrentemente (cooperativo). spawn cria tarefas separadas no runtime.
O que é segurança de cancelamento?
Um future é cancel-safe se descartá-lo em um ponto de await não perder dados necessários - crítico para loops select!.
try_join! com erros diferentes?
Use Result com um tipo de erro comum ou mapeie os erros antes do join.
select! com canais?
Padrão comum: branches recv() para desligamento, trabalho e timers em loops de servidor.
race sem select?
futures::future::select aceita dois futures - menos ergonômico que a macro para muitos branches.
timeout vs sleep em select?
timeout envolve um future; select! pode competir contra branches arbitrários, incluindo timers.
JoinHandle em select?
Use await em JoinHandle como um branch para competir a conclusão da tarefa contra um sinal de desligamento.
Streams e select?
tokio::select! suporta some_stream.next() => para iteração assíncrona multiplexada.
Testando joins?
Use tokio::time::pause e advance para controlar o tempo virtual em testes.
Desligamento gracioso do Axum?
select! entre o loop de aceitação do servidor e o sinal ctrl_c() - veja a página de Desligamento Gracioso.
Relacionado
- Básicos de Async - primeiro join
- select! & Concorrência - padrões Tokio select
- Cancelamento & Timeouts - prazos
- Tarefas & JoinHandles - joins criados
- Desligamento Gracioso - select de desligamento
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+.