Laço, thread, async ou processo
Quatro modelos de concorrência, o que cada um resolve, e o GIL que decide metade da escolha.
A pergunta que decide o modelo não é "quero paralelismo?", e sim "o trabalho espera ou calcula?". Esperar (rede, disco, banco) sobrepõe bem em uma thread; calcular exige núcleos de verdade, e no CPython isso quer dizer processos.
| Modelo | Serve para | Custo | Não serve para |
|---|---|---|---|
Arcane.Laco | milhares de conexões esperando | 1 thread, ~1 MB | conta pesada (trava o laço) |
thread: / parallel: | poucas esperas simultâneas, código simples | ~36 MB para mil threads | milhares de conexões; conta pesada (GIL) |
async / await | E/S concorrente com código sequencial | uma thread por tarefa | conta pesada (GIL) |
P.map_processos | conta pesada em vários núcleos | ~50 ms de partida por processo | trabalho pequeno (a partida domina) |
| ator | estado que várias threads alteram | 1 thread por ator | — |
Medido#
| Conexões | Laço | Thread por conexão |
|---|---|---|
| 1000 | 73 ms · 1 thread · +1 MB | 83 ms · 1000 threads · +36 MB |
| 2000 | 151 ms · 1 thread · +0 MB | 161 ms · 2000 threads · +36 MB |
O tempo quase empata — e esse é o número honesto. O que muda é a forma da conta: a memória e as threads do laço ficam planas, e as do modelo por conexão crescem em linha reta até algo quebrar.