Cancelamento e Timeouts
Em Rust async, o cancelamento é cooperativo: descartar um future ou chamar JoinHandle::abort para o trabalho no próximo ponto de await. Combine timeouts e sinais de desligamento para que os serviços falhem rapidamente e sejam desligados de forma limpa.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use tokio::time::{timeout, Duration};
async fn work() -> &'static str {
"done"
}
#[tokio::main]
async fn main() {
match timeout(Duration::from_secs(1), work()).await {
Ok(v) => println!("{v}"),
Err(_) => println!("timed out"),
}
}Quando usar isso: Prazos de cliente HTTP, desligamento gracioso, limites de trabalho por escopo de solicitação e prevenção de tarefas travadas.
Exemplo de Trabalho
use tokio::sync::watch;
use tokio::time::{sleep, Duration};
async fn worker(mut shutdown: watch::Receiver<bool>) {
loop {
tokio::select! {
_ = shutdown.changed() => {
if *shutdown.borrow() {
println!("shutting down");
break;
}
}
_ = sleep(Duration::from_millis(100)) => {
println!("tick");
}
}
}
}
#[tokio::main]
async fn main() {
let (tx, rx) = watch::channel(false);
let handle = tokio::spawn(worker(rx));
sleep(Duration::from_millis(250)).await;
tx.send(true).unwrap();
handle.await.unwrap();
}O que isso demonstra:
- Canal
watchtransmite o sinal de desligamento. select!compara ticks de trabalho com o desligamento.- A tarefa sai do loop cooperativamente quando cancelada.
Mergulho Profundo
Como Funciona
- Drop = cancelamento: Descartar um future não aguardado cancela o trabalho em andamento nos limites de
await. abort:JoinHandle::abortcancela tarefas geradas de forma semelhante.timeout: Envolve um future; ao expirar, o future interno é descartado.- Tokens de desligamento:
tokio_util::sync::CancellationTokenpropaga o cancelamento para tarefas filhas.
Ferramentas de Cancelamento
| Ferramenta | Escopo |
|---|---|
| Descartar future | Cadeia de await única |
handle.abort() | Tarefa gerada |
timeout / sleep | Operações com tempo limitado |
CancellationToken | Árvore de tarefas relacionadas |
Notas de Rust
use tokio_util::sync::CancellationToken;
async fn scoped(token: CancellationToken) {
let child = token.child_token();
let _guard = token.drop_guard();
tokio::select! {
_ = token.cancelled() => {}
_ = do_work(child) => {}
}
}- Execute a limpeza em implementações
Dropou padrõesdeferquando o cancelamento precisar liberar recursos. - Documente quais operações são seguras para cancelamento em loops
select!. - Manipuladores de solicitação Axum cancelam quando os clientes se desconectam (future descartado).
Armadilhas
- Assumir parada imediata: Loop intensivo em CPU sem
awaitignora o cancelamento. Correção: Insirayield_now().awaitou verifique o token de cancelamento em loops. - Limpeza perdida no drop: Transações de banco de dados deixadas abertas. Correção:
scopeguard, padrõesasyncexplícitos definally, ouCancellationTokencom branch de limpeza. - Timeout muito agressivo: Interrompe consultas lentas, mas válidas. Correção: Divida os timeouts (conexão vs. leitura vs. total) e tente novamente operações idempotentes.
select!recv não seguro para cancelamento: O descarte de um branch pode perder mensagens. Correção: Usebiasede leia a documentação do Tokio; use buffer ouwatch/broadcastapropriadamente.- Ignorar
JoinErrorapósabort:Erresperado - não trate como falha. Correção: Usematchcomis_cancelled()no erro.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
Apenas timeout | Prazo de operação única | Desligamento em todo o processo |
watch / broadcast | Sinal de desligamento global | Apenas cancelamento por solicitação |
CancellationToken | Árvores de tarefas pai-filho | Timeout de uso único |
| Matar processo | Código inseguro travado | Caminho de limpeza async normal |
FAQs
O cancelamento é automático?
Sim, quando os futures são descartados, mas o trabalho é executado até o próximo .await, a menos que você verifique os tokens de cancelamento em loops apertados.
Desconexão do cliente em Axum?
O future do manipulador é descartado - projete efeitos colaterais idempotentes ou use tarefas desanexadas com cuidado.
timeout vs sleep em select?
timeout cancela o trabalho envolvido; sleep apenas dispara um branch de temporizador.
CancellationToken vs watch?
CancellationToken é projetado para árvores de cancelamento; watch é um simples snapshot booleano para desligamento.
sqlx query cancel?
Descartar o future de busca cancela a consulta em andamento em drivers suportados - verifique para o seu banco de dados.
reqwest timeout?
Defina Client::builder().timeout(...) mais substituições por solicitação para prazos em camadas.
Testando cancelamento?
Gere uma tarefa, aborte, afirme flags de limpeza; use tokio::time::pause para timeouts.
Gracioso vs imediato?
Gracioso drena o trabalho em andamento; abort imediato para no próximo await - escolha por estratégia de implantação.
Mutex no caminho de cancelamento?
Libere locks em Drop ou branch de limpeza explícito para evitar estado corrompido.
Concorrência Estruturada?
O pai aguarda todos os filhos - descartar o pai cancela os filhos ao usar padrões de tarefas com escopo.
Relacionados
- Combinando Futures - select e timeout
- Desligamento Gracioso - desligamento em produção
- Temporizadores e Intervalos - sleep e interval
- Tarefas e JoinHandles - abort
- Melhores Práticas Async - design seguro para cancelamento
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+.