Paralelismo x Concorrência
Particularmente essas duas palavras quando eu ouvi a primeira vez me remetiam algo bem próximo, porém tem algumas diferenças bem claras entre esses dois paradigmas da programação.
Concorrência
É o processo que ocorre quando fazemos varias coisas mas uma por vez, ou seja como exemplo podemos dizer que quando estamos digitando um texto ou mesmo programado e tomamos um copo de água que esta em cima da mesa e depois voltamos a escrever o texto ou código estamos fazendo várias coisas mas de forma concorrente.
Paralelismo
Já quando queremos nos referir ao processo de paralelismo, podemos imaginar o cenário de que você está digitando o seu texto e ao mesmo tempo você está enxergando o que está escrevendo e também ouvindo os sons a sua volta, dessa forma está fazendo todas as coisas literalmente ao mesmo tempo e isso é um exemplo de paralelismo.
Tecnicamente:
Usei exemplos mais cotidianos para explicar o processo, porém para o computador, podemos dizer que quando estamos executando uma função que seja concorrente ela vai usar o mesmo núcleo para executar todas as tarefas que estão agendadas para acontecer no script, porém quando utilizamos uma função que utiliza o paralelismo conseguimos fazer várias tarefas literalmente de forma simultânea ou seja, cada tarefa vai utilizar um núcleo, assim fazendo o processo todo ao mesmo tempo.
Vou exemplificar com dois trechos de código utilizando golang como code example:
Concorrência:
package main
import (
"fmt"
"time"
)
func task(name string) {
for i := 0; i < 5; i++ {
fmt.Println(name, ":", i)
time.Sleep(1 * time.Second)
}
}
func main() {
go task("goroutine 1")
go task("goroutine 2")
time.Sleep(4 * time.Second)
fmt.Println("All tasks done!")
}
output:
goroutine 2 : 0
goroutine 1 : 0
goroutine 1 : 1
goroutine 2 : 1
goroutine 2 : 2
goroutine 1 : 2
goroutine 1 : 3
goroutine 2 : 3
All tasks done!
Paralelismo:
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func task(name string, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 3; i++ {
fmt.Println(name, "is working!", i)
time.Sleep(time.Second)
}
}
func main() {
runtime.GOMAXPROCS(2)
var wg sync.WaitGroup
wg.Add(2)
go task("task1", &wg)
go task("task2", &wg)
wg.Wait()
fmt.Println("All tasks are done!")
}
output:
task1 is working! 0
task2 is working! 0
task1 is working! 1
task2 is working! 1
task1 is working! 2
task2 is working! 2
All tasks are done!
Agora que sabemos como funciona o processo de concorrência e paralelismo, parece uma ótima ideia usar todas tarefas como paralelas?
na verdade não, para certos casos o paralelismo é uma ótima opção, por exemplo quando temos tarefas que precisam de um altor poder computacional, utilizar vários núcleos vai acelerar o processo com certeza, porém o sistema precisará de múltiplos núcleos disponíveis, mas em sistemas que tem menos núcleos disponíveis alternar entre “goroutines” vai ser melhor no final das contas do que forças o paralelismo, já que consome menor recursos.
Conclusão:
Concorrência é uma ótima escolha para tarefas de I/O ou que necessitem de tempo de espera e o Paralelismo vai ser muito bom para alto desempenho da CPU, coisas que exigem uma computação mais robusta, porém para ter o melhor desempenho de paralelismo seu hardware precisa atender aos requisitos que façam do paralelismo uma boa escolha.