Concorrência em Rust: threads, goroutines, async/await — análise completa

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Concorrência em Rust: de mutexes a async/await



Quando se fala em sistemas de alto desempenho, Rust instantaneamente se torna o centro das atenções. Mas o que o torna verdadeiramente único? É a concorrência em Rust — uma abordagem onde você obtém a velocidade do C e a segurança de memória, sem sacrificar a legibilidade do código. Neste artigo, vamos analisar as três principais ferramentas: threads clássicas, goroutines leves (no contexto do Rust, tarefas assíncronas) e o moderno async/await. Vamos lá!



Por que Rust não tem medo da concorrência?

Ao contrário de muitas linguagens, o Rust resolve o problema de condições de corrida (data races) no nível do compilador. O sistema de ownership (propriedade) e borrowing (empréstimo) garante que você não conseguirá modificar dados acidentalmente a partir de duas threads simultaneamente. Isso não é mágica — é uma verificação estática rigorosa. É por isso que o multithreading em Rust é considerado um dos mais seguros.



1. Threads (std::thread)

As threads clássicas do SO são a base. Em Rust, elas são implementadas através do módulo std::thread. Cada thread é um processo leve separado que pode ser executado em paralelo em diferentes núcleos da CPU.



Exemplo: iniciando threads

use std::thread;use std::time::Duration;

fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("Thread: {}", i); thread::sleep(Duration::from_millis(1)); } });

for i in 1..5 { println!("Thread principal: {}", i); thread::sleep(Duration::from_millis(1)); }

handle.join().unwrap();}


Aqui, thread::spawn cria uma nova thread, e join aguarda sua conclusão. Simples, mas há uma nuance: as threads são "pesadas". Cada thread consome pelo menos 2-8 MB de memória de pilha. Para milhares de tarefas simultâneas, isso não é adequado.



Quando usar threads?

  • Quando é necessário paralelismo real (tarefas CPU-bound).
  • Quando o número de threads não excede o número de núcleos * 2.
  • Quando você trabalha com operações bloqueantes (leitura de arquivos, chamadas síncronas).


2. Goroutines em Rust: mito ou realidade?

Estritamente falando, Rust não possui goroutines no mesmo sentido que Go. Mas possui tarefas assíncronas (tasks), que se comportam quase da mesma forma: são leves, alternam cooperativamente (sem envolvimento do SO) e podem ser executadas aos milhares em uma única thread. Para isso, utiliza-se o runtime Tokio ou async-std.



Exemplo: tarefa assíncrona (goroutine) via Tokio

use tokio::task;

#[tokio::main]async fn main() { let handle = task::spawn(async { for i in 1..10 { println!("Goroutine: {}", i); tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; } });

for i in 1..5 { println!("Principal: {}", i); tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; }

handle.await.unwrap();}


Observe: task::spawn cria uma tarefa leve, que é executada dentro do pool de threads do Tokio. O custo de memória é de cerca de 2-3 KB por tarefa. Isso permite executar 100.000 "goroutines" sem problemas. Ao contrário do Go, aqui não há um escalonador embutido — você escolhe o runtime.



Como as goroutines do Rust diferem das do Go?

  • Em Rust — as tarefas são cooperativas: elas próprias cedem o controle através de .await.
  • Em Go — as goroutines são preemptivas: o escalonador as interrompe em pontos de E/S.
  • Rust oferece mais controle: você pode escolher um runtime single-thread ou multi-thread.


3. Async/await: o coração do Rust assíncrono

As palavras-chave async e .await transformam código síncrono em assíncrono sem

Blogs

Book Recommendations