Pular para o conteúdo

O livro · 06

Processos e channels

book.md · 83 linhas · 2 min de leitura

Objetivo: criar processos com spawn, fazer eles conversarem por channels tipados, e esperar vários ao mesmo tempo com o match sem sujeito (o “select”).

A unidade de concorrência é o processo: isolado (heap próprio), barato, gerenciado pelo runtime. Cria-se com spawn (como o go do Go):

spawn log_metric(x) // fire-and-forget: sem custo, sem referência
spawn worker(data) catch |e| { ... } // com handler de falha (a morte vira o |e|; ver cap. 07)

Processos não compartilham memória (não existe @shared). Eles conversam por channel.

O channel é tipado pela perspectiva de quem segura o handle: <-T = “eu envio T”, ->T = “eu recebo T”. Isso elimina a inversão de tipos na assinatura do outro lado:

c: Channel[<-int, ->string] // eu envio int, recebo string
spawn worker(c) // o compilador checa que os dois lados são complementares
fn worker(c: Channel[<-string, ->int]) { // o worker: envia string, recebe int
val := -> c // receive (bloqueia até chegar)
c <- val.to_string() // send (async, fire-and-forget)
}

Operadores: chan <- data (send), data := -> chan (receive), e o sync request-reply com timeout obrigatório:

reply := server <-> req timeout(5s) catch |e| { return e } // Timeout | ProcessDown

O timeout é obrigatório no <-> porque dois processos fazendo <-> um no outro dariam deadlock garantido: o prazo é a saída estrutural. E note que não há coloring: o <-> só suspende o processo atual; o scheduler cuida do resto.

O mesmo match, sem valor entre match e {, espera o primeiro channel pronto:

match {
msg := -> chan_a => handle_a(msg)
msg := -> chan_b => handle_b(msg)
} timeout(100ms) {
deu_timeout() // 'timeout' é trailer do select; timeout(0) = poll
}

Sem Future/await: para N operações em paralelo, você spawna N processos e recebe os resultados:

spawn fetch("https://a", chan_a)
spawn fetch("https://b", chan_b)
ra := -> chan_a // bloqueia só quando necessário
rb := -> chan_b

spawn é fire-and-forget e não retorna nada. Para inspecionar um processo depois, registre-o por nome (estável através de restart): não há pid cru.

@register("db_writer")
spawn db_writer(conn)
p := runtime.process("db_writer") // Optional[Process], por nome
me := runtime.self() // a própria referência, de dentro de qualquer processo

Próximo: 07 · Resiliência