Concorrência
async/await, threads, canais e o que ainda não existe.
async / await#
async action buscar_usuario(id):
yield {"id": id, "nome": $"Usuario {id}"}
usuario := await buscar_usuario(7)
out usuariothread#
resultados := []
thread:
cycle i from 1 to 3:
resultados.append($"A{i}")
wait 200 # espera as threads
out len(resultados)O bloco roda numa thread daemon: o programa não espera por ela. Se terminar antes, a thread é interrompida no meio.
A condição de corrida#
Este é o ponto mais importante desta página:
contador := {"valor": 0}
thread:
cycle _ in range(0, 1000):
contador["valor"] := contador["valor"] + 1
thread:
cycle _ in range(0, 1000):
contador["valor"] := contador["valor"] + 1
wait 400
out contador["valor"] # deveria ser 2000. Frequentemente é menos.contador["valor"] + 1 são três passos — ler, somar, escrever. Se as duas threads leem 5 ao mesmo tempo, ambas escrevem 6. Um incremento se perdeu.
Rode várias vezes: o número muda. É o tipo de bug que passa em teste e quebra em produção sob carga.
channel — a via segura#
DataForge 4.0 não tem mutex nem lock. A solução não é sincronizar o acesso — é não compartilhar:
channel parciais
thread:
soma := 0 # variável local desta thread
cycle _ in range(0, 1000):
soma += 1
parciais.send(soma) # reporta uma vez, no fim
thread:
soma := 0
cycle _ in range(0, 1000):
soma += 1
parciais.send(soma)
wait 400
total := 0
persist yes:
parcial := parciais.receive()
given parcial is void:
halt
total += parcial
out total # 2000, sempreCada thread trabalha no próprio escopo. Ninguém escreve onde outro lê. Essa ideia tem nome — "não comunique compartilhando memória; compartilhe memória comunicando" — e é o lema de Go.
Regra prática#
| Situação | Seguro? |
|---|---|
| threads só leem dados compartilhados | sim |
| cada thread escreve numa variável própria | sim |
threads enviam por channel | sim |
| duas threads escrevem na mesma variável | não |
lista.append de duas threads | não |
parallel#
parallel:
saidas.append(tarefa_a())
saidas.append(tarefa_b())Cada instrução do bloco vai para uma thread — não uma thread para o bloco inteiro. É uma limitação conhecida; enquanto isso, mantenha cada linha autossuficiente.
O que ainda não existe#
Mutex,Semaphore,Atomicreceivebloqueante — hoje devolvevoidna hora se a fila estiver vaziaTaskGroupe cancelamentoparalleltratando blocos em vez de instruções
Tudo isso está no roadmap.