Filas de tarefas para Rust: visão geral, exemplos e recomendações
Rust — uma linguagem de programação de sistemas que está sendo cada vez mais usada para criar serviços web de alta carga, microsserviços e ferramentas de processamento de dados. Um dos elementos-chave de tais sistemas é a fila de tarefas (task queue), que permite distribuir eficientemente o trabalho entre threads ou tarefas assíncronas. Neste artigo, veremos bibliotecas populares para trabalhar com filas de tarefas em Rust, suas características, desempenho e exemplos de código.
Para que servem as filas de tarefas?
As filas de tarefas resolvem vários problemas:
- Balanceamento de carga — as tarefas são distribuídas entre os workers.
- Processamento assíncrono — operações longas não bloqueiam a thread principal.
- Tolerância a falhas — se um worker falhar, a tarefa pode ser reexecutada.
- Escalabilidade — é fácil adicionar novos workers.
Principais bibliotecas para filas de tarefas em Rust
1. tokio com canais mpsc e broadcast
tokio — é um runtime assíncrono, o padrão de facto para Rust. Para filas de tarefas, são usados canais multiprodutor, um consumidor (mpsc) ou de broadcast (broadcast).
Exemplo:
use tokio::sync::mpsc;
#[tokio::main]async fn main() { let (tx, mut rx) = mpsc::channel(100);
// Produtor tokio::spawn(async move { for i in 0..10 { tx.send(i).await.unwrap(); } });
// Consumidor while let Some(msg) = rx.recv().await { println!("Recebido: {}", msg); }}Vantagens: alto desempenho, integração com o ecossistema Tokio, suporte a backpressure.
Desvantagens: requer contexto assíncrono, não é adequado para tarefas síncronas.
2. async-std com canais
Análogo ao tokio, mas com uma API diferente. Usa async-channel ou flume internamente.
Exemplo:
use async_std::task;use async_channel::unbounded;
fn main() { task::block_on(async { let (s, r) = unbounded(); s.send(42).await.unwrap(); let val = r.recv().await.unwrap(); println!("Valor: {}", val); });}Vantagens: API simples, suporte a operações assíncronas e síncronas.
Desvantagens: menos popular que o Tokio.
3. flume — canais rápidos sem dependências
flume — é uma biblioteca que fornece canais multiprodutor, multiconsumidor (MPMC). Ela não requer um runtime assíncrono e pode ser usada tanto em código síncrono quanto assíncrono.
Exemplo:
use flume;
fn main() { let (tx, rx) = flume::unbounded();
std::thread::spawn(move || { tx.send("Olá da thread").unwrap(); });
let msg = rx.recv().unwrap(); println!("{}", msg);}Vantagens: muito rápida (comparável ao crossbeam), simples, suporta async.
Desvantagens: não possui suporte embutido a backpressure (para canais bounded existe).
4. crossbeam — primitivos de baixo nível
crossbeam — um conjunto de ferramentas para programação paralela. Contém canais (crossbeam-channel) que são alguns dos mais rápidos em Rust.
Exemplo:
use crossbeam_channel::unbounded;
fn main() { let (s, r) = unbounded(); s.send("Hello").unwrap(); let msg = r.recv().unwrap(); println!("{}", msg);}Vantagens: desempenho máximo