🎓 Módulo 1: Fundamentos de Computação Concorrente na BEAM

Objetivo: Compreender o modelo de execução da Máquina Virtual Erlang (BEAM) e contrastá-lo com os modelos tradicionais de Threads de SO e Green Threads (Go).

1. O Problema da Concorrência Tradicional

Na engenharia de software clássica (Java, C++, C#), a unidade de concorrência é a Thread do Sistema Operacional (OS Thread). • Peso: Cada thread consome uma quantidade significativa de memória (geralmente alguns MBs apenas para a stack). • Gerenciamento: O Kernel do SO é responsável por agendar essas threads (Context Switching). Trocar de contexto no nível do kernel é uma operação custosa (latência). • Memória Compartilhada: Threads do mesmo processo compartilham o mesmo espaço de memória (Heap). ◦ O Perigo: Se a Thread A e a Thread B tentam escrever na variável x ao mesmo tempo, ocorre uma Condição de Corrida (Race Condition). ◦ A Solução (e o problema dela): Usamos Mutexes, Semáforos e Locks. Isso introduz complexidade e riscos de Deadlocks.

2. A Solução Elixir: O Modelo de Atores e a BEAM

O Elixir roda sobre a BEAM. A BEAM não utiliza threads do SO diretamente para executar sua lógica. Ela utiliza Processos Leves (Lightweight Processes).

2.1. O Processo da BEAM

• Peso: Extremamente leve (inicia com cerca de ~300 words ou ~2.5KB). Você pode rodar milhões deles em um laptop. • Isolamento de Memória (Share Nothing): Cada processo tem sua própria Heap e Stack. O Processo A não consegue acessar a memória do Processo B. • Comunicação: A única forma de interação é através de Troca de Mensagens (Message Passing). Os dados são copiados de um processo para outro (com exceção de binários grandes, que usam contagem de referência).

2.2. O Scheduler da BEAM (Escalonamento)

Isso é crucial para engenheiros: A BEAM roda, geralmente, uma Thread de SO por Núcleo de CPU. Dentro dessas threads, a BEAM roda seus próprios escalonadores (Schedulers). • Escalonamento Preemptivo: A BEAM atribui a cada processo um número de "reduções" (aproximadamente 2000 chamadas de função). Quando as reduções acabam, a BEAM pausa o processo forçadamente e dá a vez para o próximo. ◦ Vantagem: Um processo com loop infinito ou cálculo pesado não trava o sistema. Em Node.js (single-thread event loop), um cálculo pesado trava tudo. No Elixir, não.

3. Comparativo Técnico: Elixir vs. Go (Goroutines) vs. Java (Threads)

Esta é uma dúvida comum de mercado e academia.

Característica Java / C# (OS Threads) Go (Goroutines) Elixir (BEAM Processes)
Gerenciamento Kernel do SO Runtime da linguagem (M:N) Runtime da linguagem (M:N)
Uso de Memória Alto (MBs) Baixo (KBs) Muito Baixo (KBs)
Memória Compartilhada (precisa de mutex) Compartilhada (mas encoraja channels) Isolada — Share Nothing
Tolerância a Falhas Se uma thread crashar, o processo pode cair. Se houver panic não tratado, o programa cai. Se um processo crashar, só ele morre. O sistema continua.
Latência Previsível (limitada pelo SO) Baixa Soft Real-Time (latência muito previsível)

Nota para o Engenheiro

Goroutines (Go) e Processos Elixir (BEAM) são ambos green threads, mas com filosofias opostas:

4. Conceitos Fundamentais

Precisamos alinhar o vocabulário para as próximas etapas.

4.1. Concorrência vs. Paralelismo

• Concorrência: É sobre a estrutura do programa. É a capacidade de lidar com muitas coisas ao mesmo tempo. (Ex: Um servidor web aceitando 10 mil conexões). • Paralelismo: É sobre a execução física. É fazer muitas coisas no mesmo instante de tempo. Requer múltiplos núcleos de CPU. ◦ Em Elixir: Você escreve código concorrente (muitos processos). A BEAM se encarrega de paralelizar isso automaticamente em todos os núcleos disponíveis.

4.2. Síncrono vs. Assíncrono

Em sistemas distribuídos (e no Elixir): • Síncrono (Call): Envio a mensagem e bloqueio minha execução até receber a resposta (Request/Response). • Assíncrono (Cast): Envio a mensagem e sigo minha vida ("Fire and forget"). Não sei se a mensagem chegou ou se foi processada na hora.

4.3. Transparência de Localização (Sistemas Distribuídos)

Como processos se comunicam apenas por mensagens enviadas para um endereço (PID - Process ID), não importa onde esse processo está. • Enviar mensagem para o PID <0.100.0> (na mesma máquina). • Enviar mensagem para o PID <50.100.0> (em um servidor no Japão). O código é exatamente o mesmo. Isso torna o Elixir uma linguagem naturalmente orientada a sistemas distribuídos (Clusters).

🧪 Módulo 2: Laboratório de Anatomia de Processos

Objetivo: Manipular as primitivas de concorrência da BEAM (spawn, send, receive) e entender o ciclo de vida de um processo.

1. Preparação do Ambiente

Abra seu terminal. Vamos criar um projeto chamado lab_concorrencia. Isso carrega o ambiente do Mix, que será útil para compilar os módulos que criaremos mais à frente.

mix new lab_concorrencia
cd lab_concorrencia
iex -S mix