Pular para o conteúdo principal

Concorrência & Async

Os modelos de concorrência são um dos maiores diferenciadores entre linguagens de programação. Veja como diferentes linguagens lidam com programação assíncrona, threads e execução concorrente.

Execução Básica Async / Concorrente​

// Promises
const fetchData = () => {
return new Promise((resolve) => {
setTimeout(() => resolve("data"), 1000);
});
};

// Async/await
async function main() {
const result = await fetchData();
console.log(result);
}

// Promise.all (parallel)
const [a, b] = await Promise.all([
fetchData(),
fetchData(),
]);

// Promise.race (first to complete)
const fastest = await Promise.race([
fetchData(),
fetchData(),
]);

Segurança de Threads & Sincronização​

// JavaScript is single-threaded (event loop)
// No shared memory issues in main thread

// Web Workers (browser) / Worker Threads (Node.js)
const { Worker } = require('worker_threads');

const worker = new Worker('./worker.js');
worker.postMessage({ data: 'hello' });
worker.on('message', (msg) => {
console.log('From worker:', msg);
});

// SharedArrayBuffer (shared memory)
const buffer = new SharedArrayBuffer(1024);
const view = new Int32Array(buffer);
Atomics.add(view, 0, 1); // Atomic operation

Principais Conclusões​

  • Modelos de concorrência variam amplamente -- Go usa goroutines com channels (estilo CSP): go func() { ch <- "hello" } e msg := <-ch. JavaScript, Python, Rust, C# e Swift usam async/await: async def fetch_data() em Python, async fn fetch_data() em Rust com tokio, async Task<string> FetchDataAsync() em C#. C, C++ e Java usam threads tradicionais do SO com pthread_create, std::thread ou Thread.start(), além de mutexes para sincronização. Goroutines são leves (milhões possíveis); threads são mais pesadas. Async/await é adequado para trabalho I/O-bound; threads são adequadas para paralelismo CPU-bound. Escolha Go para concorrência simples; escolha async/await para servidores pesados em I/O; escolha threads quando precisar de paralelismo real e controle.

  • Comunicação baseada em channels é nativa em Go -- Os channels embutidos do Go e o select tornam padrões produtor-consumidor idiomáticos: ch := make(chan string), ch <- "msg", msg := <-ch e select { case msg := <-ch1: ... case msg := <-ch2: ... }. Rust oferece std::sync::mpsc::channel() e C# tem Channel.CreateUnbounded<T>(), mas são recursos de biblioteca. O design do Go encoraja "compartilhar memória comunicando" em vez de usar mutexes. Se você constrói pipelines, pools de workers ou sistemas orientados a eventos, os channels do Go são os mais ergonômicos; Rust e C# fornecem poder similar com mais configuração.

  • Go e Rust se destacam em concorrência -- As goroutines do Go são leves (pilhas de 2KB, multiplexadas em threads do SO) e fáceis de criar: go worker(id). O modelo de ownership do Rust previne data races em tempo de compilação: o compilador rejeita compartilhamento de estado mutável entre threads sem sincronização. Por exemplo, Arc<Mutex<i32>> em Rust garante acesso compartilhado seguro. Go depende de channels e disciplina; Rust garante segurança estaticamente. Escolha Go quando quiser concorrência simples e rápida com fricção mínima; escolha Rust quando precisar de máxima segurança e performance sem coletor de lixo.

  • Swift introduz atores -- Atores fornecem estado compartilhado thread-safe embutido ao serializar o acesso: actor Counter { private var count = 0; func increment() { count += 1 } } -- todos os métodos executam serialmente, então não há data races. Você chama com await account.deposit(100). Isso substitui padrões manuais de Mutex/Lock. Os atores do Swift se integram com async/await e concorrência estruturada. Se você constrói sistemas concorrentes em Swift (ex.: iOS/macOS), prefira atores em vez de locking manual; para outras linguagens, use mutexes ou designs baseados em channels.

  • JavaScript é single-threaded -- O event loop lida com async via callbacks e Promises: await fetch('/api') não bloqueia a thread; ela cede até a resposta chegar. Paralelismo real requer Web Workers (navegador) ou Worker Threads (Node.js) com postMessage para comunicação. Trabalho pesado de CPU (hashing, processamento de imagem) bloqueia a thread principal; delegue-o para workers. Para servidores Node.js I/O-bound, async/await é suficiente; para tarefas CPU-bound, adicione workers ou considere uma linguagem multi-threaded como Go ou Rust.