GO: Paralelismo e concorrência
Tags: braziliandevs, beginners, todayilearned, go
Published: 2022-05-29 01:55:21

Paralelismo e concorrência em GO foi na verdade o primeiro assunto que me fez ter interesse em aprender essa linguagem, apesar do meu objetivo inicial ter sido ver como ela tratava o paralelismo só hoje que eu fui de fato estudar sobre.

## Diferenças entre concorrência e paralelismo

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i95jkm2f3ikvfmonska0.png)

Na concorrência os processos são disparados ao mesmo tempo mas como é usado apenas um core, um processo vai parar para que o outro possa rodar e ai eles vão sendo intercalados.

No paralelismo os dois processos são iniciados ao mesmo tempo, mas cada um é processado em um core diferente do processador, fazendo assim com que os dois possam ser processados simultaneamente
 
## Go routines

```GO
  func main() {
	runProcess("Process 1", 20)
	runProcess("Process 2", 20)

}

func runProcess(name string, total int) {
	for i := 0; i < total; i++ {
		fmt.Println(name, i)
		t := time.Duration(rand.Intn(255))
		time.Sleep(time.Millisecond * t)
	}
}

```

Essa função definida acima dessa forma inicial vai primeiro printar 20 vezes no terminal "Process 1" esperando sempre um tempo aleatório em milissegundos para o segundo print, e depois vai printar mais 20 vezes "Process 2".

Adicionando um operador `go` na frente de cada uma da chamada das funções vai fazer com que ela rodem em paralelo e em background. Agora ao invés de esperarmos a primeira função rodar para só então a segunda ser executada, com o `go` na frente enquanto uma função está esperando o timeout passar a outra é executada (se não definíssemos um timeout como é uma função muito simples na hora que o programa chegasse na segunda função a primeira já teria terminado de executar e não daria para ver o efeito da go routine)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aohg5l73sxa4znwu70w2.png)

porém se somente adicionar o `go` na frente da função o terminal não vai exibir nada, por que as funções estão rodando em background então na verdade o go vai achar que elas já terminaram de rodar e terminar a execução. Para resolver esse problema precisamos criar um waitGroup e para fazer com que o Go espere a função terminar de rodar para ai sim matar a aplicação.

```go

var waitGroup sync.WaitGroup

func main() {
	waitGroup.Add(2)

	go runProcess("Process 1", 20)
	go runProcess("Process 2", 20)

	waitGroup.Wait()

}

func runProcess(name string, total int) {
	for i := 0; i < total; i++ {
		fmt.Println(name, i)
		t := time.Duration(rand.Intn(255))
		time.Sleep(time.Millisecond * t)
	}
	waitGroup.Done()
}
```
No main adicionei duas funções ao waitGroup e na função runProcess falo que o waitGroup concluiu o trabalho depois do for.

Mas agora fica a questão, o Go está usando paralelismo ou concorrência? e na verdade depende, nesse caso é paralelismo pois tenho uma CPU de 6 cores então por default ele associa cada função a um core, porem se eu tivesse apenas 1 core no processador ele rodaria as duas funções de forma concorrente

podemos testar também rodar de forma concorrente adicionando uma função init e setando o numero máximo de cores que permitimos o GO usar. porem se fizermos isso para esse caso o output será o mesmo

```go
func init() {
	runtime.GOMAXPROCS(1)
}
```


## Race conditions

Race conditions é um tipo de problemas que temos com o pararelismo onde a execução dos codigos em pararelo compromete de alguma forma as regras de negocio da aplicação, então por exemplo caso eu queira executar o total de vezes que o for rodou

```go
var result int
var waitGroup sync.WaitGroup

func main() {

	waitGroup.Add(2)

	go runProcess("Process 1", 20)
	go runProcess("Process 2", 20)

	waitGroup.Wait()
	fmt.Println("Result:", result)
}

func runProcess(name string, total int) {
	for i := 0; i < total; i++ {
		z := result
		z++
		t := time.Duration(rand.Intn(255))
		time.Sleep(time.Millisecond * t)
		result = z
		fmt.Println(name, "->", i, result)
	}
	waitGroup.Done()
}

```

no meu println com mostrando o result que deveria ser 40 na verdade o output vai ser 20 e podemos ver que o valor de result de fato está sendo reatribuindo para um valor anterior constantemente por causa da execução anterior ainda não ter o novo valor que result deveria ter

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhvvho8bpzbgqqxsbxr2.png)

se rodarmos o programa com `go run -race main.go` o proprio go já detecta se está ocorrendo uma race condition e se sim em quais linhas

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzijhfxjf48ju37ebv5l.png)

para resolver o problema de race condition é bem simples basta usarmos o [Mutex](https://go.dev/tour/concurrency/9) onde travamos uma operação até ela terminar de rodar para impedir que ela seja sobescrita no meio

```go
func runProcess(name string, total int) {
	for i := 0; i < total; i++ {

		t := time.Duration(rand.Intn(255))
		time.Sleep(time.Millisecond * t)
		m.Lock()
		result++
		fmt.Println(name, "->", i, "total", result)
                m.Unlock()
	}
	waitGroup.Done()
}
```

Agora os processos ficam travados esperando o result terminar de aumentar o seu valor, e só quando dou o unlock eles continuam. Se rodarmos a aplicação detectando race conditions podemos ver que agora nada foi detectado

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eeue74pb2p3injkuohbl.png)