Um Pouco Sobre Goroutines em Go!
Published: 2023-04-12 16:56:58

## Goroutines em vez de threads

Um dos principais objetivos da **linguagem de programação Go** é tornar a simultaneidade mais simples, rápida e eficiente.

Com certeza, as Goroutines são uma das principais características do Go e são uma das principais razões pelas quais o Go é tão eficiente na execução de tarefas em paralelo.

Quando criamos apis em Go utilizando net/http e recebemos uma porrada de requisições o que o pkg net/http usa? Se sua resposta for Goroutines você está correto 😍.

Eu evito o termo "threads leves", isto gera uma confusão para quem está iniciando ou tentando entender o que são Goroutines. Embora seja verdade que as Goroutines são semelhantes às "threads leves" em outros sistemas, Go usa uma implementação diferente que é otimizada para o contexto do Go. Portanto, pode ser melhor explicar as Goroutines em termos de como elas funcionam no Go que eu acredito que irá facilitar ainda mais o seu entendimento.

Em vez de dizer que as Goroutines são "threads leves", podemos dizer que as Goroutines são unidades leves de concorrência que são executadas em uma única thread de execução ou em várias dependendo de como o runtime Go decide gerenciá-las. Isso significa que o Go pode executar muitas Goroutines simultaneamente em uma única thread ou diversas threads.

As Goroutines são gerenciadas pelo runtime do Go e não pelo sistema operacional subjacente. Isso significa que as Goroutines são muito mais leves e eficientes do que as threads tradicionais do sistema operacional e que o Go pode gerenciá-las de forma mais eficaz para obter o máximo desempenho e escalabilidade.

Um dos objetivos das Goroutines em Go é permitir que você escreva programas concorrentes de maneira fácil e eficiente. Isso significa que você pode ter várias Goroutines em execução simultaneamente, cada uma executando uma tarefa diferente, sem precisar se preocupar com bloqueios de recursos, espera ativa ou outros problemas comuns de programação concorrente.

Para criar uma Goroutine em Go, você pode simplesmente adicionar a palavra-chave "go" antes de uma chamada de função. 
Por exemplo, considere o seguinte código:

```go
package main

func myFuncJeff() {
    // seu gode aqui.
}

func main() {
    // inicia a Goroutine
    go myFuncJeff()

    // pode colocar algo aqui
}
```

**Goroutines** são leves e tiram proveito de todo o poder de processamento disponível. Goroutines existe apenas no espaço virtual do tempo de execução Go e não no sistema operacional.

**Goroutine** é um método/função que pode ser executado independentemente junto com outras goroutines. Cada atividade simultânea na linguagem Go é geralmente denominada **Goroutine**.

**Go** é uma linguagem **multiparadigma** e dentre os mais relevantes é o paradigma **concorrente**. Um dos pontos mais relevantes e importantes na linguagem **Go** é o trabalho com **concorrência**, **Go** **inovou** ao quebrar o modelo **tradicional** de [*threads*](https://pt.wikipedia.org/wiki/Thread_%28computa%C3%A7%C3%A3o%29) e sua forma de utilização ao criar um novo modelo, as [*goroutines*](https://golang.org/doc/effective_go.html#concurrency). As **goroutines** são responsáveis por realizar execuções em **Go** de forma **assíncrona**. São muito poderosas e uma simples máquina de **1G** de **Ram e 1CPU** poderá subir milhares de goroutines.

Em Go as **Goroutines** contribuem para tornar a **simultaneidade** **fácil** de usar. As **goroutines** podem ser muito baratas: eles têm pouca sobrecarga além da memória para a pilha, que é apenas alguns kilobytes. 

Para tornar as pilhas pequenas, o tempo de execução de **Go** usa pilhas redimensionáveis e delimitadas. Uma **goroutine** recém lançada recebe alguns kilobytes, o que é quase sempre suficiente. Quando não é, o tempo de execução aumenta (e encolhe) a memória para armazenar a pilha automaticamente, permitindo que muitos **goroutines** vivam em uma quantidade **modesta** de memória. 

A sobrecarga da CPU tem em média três instruções baratas por chamada de função. É prático criar **centenas de milhares de goroutines** no mesmo espaço de endereço. Se **goroutines** fossem apenas **threads**, os recursos do sistema acabariam em um número muito menor.

```go
package main

func enviarEmail(...string) {
	// Código para enviar o e-mail
}

func enviarParaFila(mensagem string) {
	// Código enviando para fila
}

func main() {
	// goroutine envia email
	go enviarEmail("jeff@gmail.com", "meu email")

	// goroutine envia para fila
	go enviarParaFila("Mensagem para a fila")

	// Faça alguma outra coisa aqui
}

```
Neste exemplo, criamos duas funções, enviarEmail e enviarParaFila, que simulam o envio de um e-mail e o envio de uma mensagem para a fila, respectivamente. Em seguida, na função main(), iniciamos duas Goroutines diferentes, uma para enviar um e-mail e outra para enviar uma mensagem para a fila.

Observe que estamos usando a palavra-chave go antes de cada chamada de função para iniciar uma Goroutine separada para cada tarefa. Isso significa que o programa continuará a ser executado normalmente, sem esperar que o envio de e-mail ou a mensagem da fila sejam concluídos.

O design de **Go** foi fortemente influenciado pelo artigo de [C.A.R. Hoare](https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf) **1978** “[Communicating sequential processes](https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf)” (*Comunicação de processos sequenciais*).

## Simultaneidade nas ideias do CSP

Um dos modelos mais bem-sucedidos para fornecer suporte linguístico de alto nível para concorrência é o ***Hoare’s*** ***Communicating Sequential Processes***, ou [*CSP*](https://en.wikipedia.org/wiki/Communicating_sequential_processes)*,* [*Occam*](https://pt.wikipedia.org/wiki/Occam_%28linguagem_de_programa%C3%A7%C3%A3o%29) e [*Erlang*](https://pt.wikipedia.org/wiki/Erlang_%28linguagem_de_programa%C3%A7%C3%A3o%29) são duas línguas bem conhecidas que derivam de [*CSP*](https://en.wikipedia.org/wiki/Communicating_sequential_processes). As primitivas de concorrência de **Go** derivam de uma parte diferente da árvore genealógica cuja principal contribuição é *a poderosa noção de canais como objetos de primeira classe*. A experiência com várias linguagens anteriores mostraram que o modelo [*CSP*](https://en.wikipedia.org/wiki/Communicating_sequential_processes) se encaixa bem em uma estrutura de linguagem procedural.

A maior diferença entre **Go** e o modelo [*CSP*](https://en.wikipedia.org/wiki/Communicating_sequential_processes), além da sintaxe, é que o **Go** modela os canais de comunicação **simultânea explicitamente como canais**, enquanto os processos da linguagem de ***Hoare*** enviam mensagens diretamente uns aos outros, semelhante ao [Erlang](https://pt.wikipedia.org/wiki/Erlang_%28linguagem_de_programa%C3%A7%C3%A3o%29).

Bem para chegar neste resultado **Go** novamente sofreu críticas do modelo adotado. **Go** incorpora uma variante do [*CSP*](https://en.wikipedia.org/wiki/Communicating_sequential_processes) (*Comunicação de processos sequenciais*) é uma linguagem formal para descrever padrões de interação em sistemas concorrentes com canais de primeira classe. Não foi adotada uma abordagem de gravação única para valorizar a semântica no contexto da [*computação concorrente*](https://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_concorrente) como é feita no [*Erlang*](https://pt.wikipedia.org/wiki/Erlang_%28linguagem_de_programa%C3%A7%C3%A3o%29), ao invés disto adotaram algo prático e que resultou em algo **poderoso**, permitindo programação **simultânea simples** e **segura**, mas *não proíbe programação incorreta*. E o lema criado foi: “***Não se comunique compartilhando memória, compartilhe memória comunicando-se***”.

A simplicidade e o suporte para concorrências oferecidas por **Go** gera robustez.

```go
package main

import (
	"fmt"
	"net/http"
	"time"
)

func getSites(url string) {
	resposta, err := http.Get(url)
	if err != nil {
		fmt.Printf("Erro ao ler %s: %s\n", url, err)
		return
	}
	defer resposta.Body.Close()
	fmt.Printf("%s lido com sucesso\n", url)
}

func main() {
	// URLs que iremos ler
	urls := []string{
		"https://www.google.com",
		"https://www.facebook.com",
		"https://www.github.com",
		"https://www.linkedin.com",
	}

	// Goroutine separada para ler cada URL
	for _, url := range urls {
		go getSites(url)
	}

	// Faça alguma outra coisa aqui
        // ...
	time.Sleep(5 * time.Second)
}
```
Neste exemplo, definimos uma função getSite que usa o pacote net/http para fazer uma solicitação HTTP a um site específico e ler a resposta. Em seguida, na função main(), definimos uma lista de URLs que queremos ler e iniciamos uma Goroutine separada para ler cada URL.

Observe que estamos usando um loop for para percorrer a lista de URLs e iniciar uma Goroutine separada para cada URL usando a palavra-chave go. Isso significa que o programa continuará a ser executado normalmente, sem esperar que cada solicitação HTTP seja concluída.

Para dar tempo às Goroutines para que elas possam fazer o seu trabalho, usamos a função time.Sleep() para suspender a execução do programa por um curto período de tempo. Neste exemplo, usamos um atraso de 5 segundos para permitir que as Goroutines terminem de executar.

Vamos fazer o mesmo código agora usando sync.WaitGroup para sincronizar as nossas Goroutines e ficar ainda melhor o controle sobre elas.

```go
package main

import (
	"fmt"
	"net/http"
	"sync"
)

func getSite(url string, wg *sync.WaitGroup) {
	// Sinalize o WaitGroup quando a Goroutine terminar
	defer wg.Done()

	resp, err := http.Get(url)
	if err != nil {
		fmt.Printf("Erro ao ler %s: %s\n", url, err)
		return
	}
	defer resp.Body.Close()

	fmt.Printf("%s lido com sucesso\n", url)
}

func main() {
	// URLs que iremos ler
	urls := []string{
		"https://www.google.com",
		"https://www.facebook.com",
		"https://www.github.com",
		"https://www.linkedin.com",
	}

	// Crie um WaitGroup 
        // para sincronizar as Goroutines
	var wg sync.WaitGroup

	for _, url := range urls {
		// Adicione WaitGroup para cada Goroutine
		wg.Add(1)

		// Passe o WaitGroup como um ponteiro
		go getSite(url, &wg)
	}

	// Espere até que todas as Goroutines terminem
	wg.Wait()

        fmt.Println("done")
}
```

Usando o WaitGroup, podemos garantir que todas as Goroutines terminem de executar antes que o programa continue. Isso é especialmente útil quando queremos que o programa termine apenas quando todas as tarefas simultâneas sejam concluídas.

## Concorrência é diferente de Paralelismo

Uma das famosas frases é: *“****Concorrência é sobre lidar com muitas coisas ao mesmo tempo. Paralelismo é fazer muitas coisas ao mesmo tempo****”.* **Concorrência** é a **composição** de cálculos de execução independente. **Concorrência** é uma maneira de estruturar software, e definitivamente não é paralelismo embora permita o paralelismo.

Se você tiver apenas um núcleo físico em seu processador, seu programa ainda pode ser concorrente, mas não pode ser paralelo. Por outro lado, um programa concorrente bem escrito pode ser executado de forma eficiente em paralelo em processador que possua mais de um núcleo físico. Sugiro dar uma conferida neste vídeo de uma palestra do [*Rob Pike*](https://pt.wikipedia.org/wiki/Rob_Pike) “[*Concorrência não é Paralelismo*](https://vimeo.com/49718712)”.

**Concorrência** em **Go** é muito poderosa e também simples de godar, está foi a intensão dos engenheiros que desenvolveram **Go**. A resolução de muitos problemas é bem mais eficiente utilizando **concorrência** e este é o **poder** de **Go**, por isto, se tornou um **Deus** quando o assunto é **concorrência**. Devido a isto, problemas englobados neste universo serão resolvidos com muita **eficiência** e o mais importante, com muito **pouco recurso computacional**.

Quer aprofundar ainda mais sobre **concorrência**? Só entrar neste [*link*](https://github.com/golang/go/wiki/LearnConcurrency). Quer testar e visualizar alguns exemplos? [*Click Aqui*](https://github.com/jeffotoni/goexample/tree/master/goroutine).

**Goroutine** é um tópico muito extenso, cheio de detalhes e muitas situações interessantes. Então requer que futuramente façamos um post somente sobre isso para que seja tratado com o devido cuidado e atenção.

Confira um exemplo simples.

```go
package main


import (
	"fmt"
	"time"
)


func main() {


	go func(){fmt.Println("oi sou uma goroutine1!")}()
	go func(){fmt.Println("oi sou uma goroutine2!")}()
	go func(){fmt.Println("oi sou uma goroutine3!")}()
	fmt.Println("Olá, estamos testando as goroutines em Go!")	
	time.Sleep(time.Second)
	
	
}
```

## Canais

Os canais em Go são uma forma de sincronizar a comunicação entre Goroutines e compartilhar dados. Eles permitem que uma Goroutine envie dados para outra Goroutine de maneira segura e eficiente, sem a necessidade de usar bloqueios ou semáforos.

Os canais são criados usando a função **make()** e podem ser usados para enviar ou receber valores de um determinado tipo. Os canais podem ser definidos com uma capacidade opcional, que especifica quantos valores podem ser armazenados no canal antes que ele comece a bloquear. Se a capacidade não for especificada, o canal terá uma capacidade de zero, o que significa que ele pode armazenar apenas um valor por vez.

Para enviar um valor para um canal, usamos a sintaxe <-, como em canal <- valor. Para receber um valor de um canal, usamos a mesma sintaxe, mas invertida, como em valor <- canal. Quando usamos a sintaxe <- sem uma variável à esquerda ou à direita, ele é usado para bloquear a execução da Goroutine até que um valor seja enviado ou recebido pelo canal.

```go
package main

import "fmt"

func produtor(canal chan<- int) {
    for i := 0; i < 10; i++ {
        canal <- i // Envia um valor para o canal
    }
    close(canal) // Fecha o canal
}

func consumidor(canal <-chan int) {
    for valor := range canal { // Itera sobre os valores recebidos do canal
        fmt.Println(valor) // Exibe o valor recebido
    }
}

func main() {
    // Cria um canal sem capacidade definida
    canal := make(chan int)

    // Goroutine do produtor para enviar valores para o canal
    go produtor(canal)

    // Goroutine do consumidor para receber os valores do canal
    consumidor(canal)
}

```
Neste exemplo, temos duas Goroutines diferentes: uma produtora e uma consumidora. A Goroutine produtora envia valores para o canal, enquanto a Goroutine consumidora os recebe e os exibe na tela.

Observe que estamos usando a palavra-chave chan para definir o tipo do canal. No caso deste exemplo, estamos usando chan int, que significa que estamos criando um canal que pode ser usado para enviar ou receber valores inteiros.

Também usamos as palavras-chave <-chan e chan<- para especificar se o canal deve ser usado para enviar ou receber valores. No caso da Goroutine produtora, estamos usando canal chan<- int, o que significa que estamos criando um canal que pode ser usado apenas para enviar valores inteiros. Na Goroutine consumidora, estamos usando canal <-chan int, o que significa que estamos criando um canal que pode ser usado apenas para receber valores inteiros.

Ao chamar a função close() no canal na Goroutine produtora, estamos sinalizando que não há mais valores para serem enviados pelo canal. Isso faz com que a função range na Goroutine consumidora saia do loop quando todos os valores tiverem sido recebidos.

Vamos reforçar o conceito: Um **canal** é um objeto de comunicação usado pelas **goroutines** e que tem o papel fundamental na comunicação **entre** as **goroutines**. Tecnicamente, um canal é a transferência de dados no qual os dados podem ser **passados** ou **lidos** . Assim, uma goroutine pode enviar dados para um canal, enquanto outras **goroutines** podem ler os dados do mesmo canal, como uma fila. Os canais é a forma mais segura de comunicação na linguagem **Go**. Existe outras formas de compartilhar dados em **Go,** não tão eficientes quanto os **canais** a equipe que desenvolveu **Go** decidiram não fechar as possibilidades e é possível compartilhar dados **sem usar canais**.

## Declarando um Canal sem buffer e com buffer

```go

type Promise struct {
  Result chan string
  Error  chan error
}

var (
  ch1  = make(chan *Promise)  // received a pointer from the structure
  ch2  = make(chan string, 1) // allows only 1 channels
  ch3  = make(chan int, 2)    // allows only 2 channels
  ch4  = make(chan float64)   // has not been set can freely receive
  ch5  = make(chan []byte)    // by default the capacity is 0
  ch6  = make(chan bool, 1)   // non-zero capacity
  ch7  = make(chan time.Time, 2)
  ch8  = make(chan struct{}, 2)
  ch9  = make(chan struct{})
  ch10 = make(map[string](chan int)) // map channel
  ch11 = make(chan error)
  ch12 = make(chan error, 2)
  // receives a zero struct
  ch14 <-chan struct{}
  ch15 = make(<-chan bool)          // can only read from
  ch16 = make(chan<- []os.FileInfo) // can only write to// holds another channel as its value
  ch17 = make(chan<- chan bool) // can read and write to
)
```

## Recebendo valores no canal

```go
func main() {

  ch2 <- "okay"
  defer close(ch2)
  fmt.Println(ch2, &ch2, <-ch2)

  ch7 <- time.Now()
  ch7 <- time.Now()
  fmt.Println(ch7, &ch7, <-ch7)
  fmt.Println(ch7, &ch7, <-ch7)
  defer close(ch7)

  ch3 <- 1 // okay
  ch3 <- 2 // okay

  // deadlock // ch3 <- 3 // does not accept any more 
  // values, if you do it will error : deadlockdefer close(ch3)

  fmt.Println(ch3, &ch3, <-ch3)
  fmt.Println(ch3, &ch3, <-ch3)

  ch10["lambda"] = make(chan int, 2)
  ch10["lambda"] <- 100defer close(ch10["lambda"])
  fmt.Println(<-ch10["lambda"])
}
```

Um pequeno exemplo de goroutine utilizando canais.

*Exemplo1:*

```go
package main

import "fmt"

func goroutine(c chan string) {
	fmt.Println("Eu sou um canal: " + <-c + "!")
}


func main() {

  fmt.Println("start goroutine!")

  c := make(chan string)

  go goroutine(c)
  
  c <- "jeffotoni"
   
}
```

*Exemplo2:*

```go
	package main
	

	import (
		"fmt"
		"time"
	)
	

	// escrevendo no canal
	func write(ch chan int) {
		for i := 0; i < 5; i++ {
			ch <- i
			fmt.Println("escrever:", i, "to ch")
		}
		close(ch)
	}
	

	func main() {
	

		// channel com buffer
		ch := make(chan int, 2)
	

		// goroutine
		go write(ch)
	

		//aguarde um pouco
		time.Sleep(1 * time.Second)
	

		// listando o canal
		for v := range ch {
			fmt.Println("ler", v, "from ch")
			time.Sleep(2 * time.Second)
		}
	}
```

Neste exemplo, estamos definindo uma variável chamada ch que é um canal que só pode ser usado para receber valores do tipo struct{}. Usamos a palavra-chave <-chan para especificar que esse canal só pode ser usado para receber valores.

Confira o código abaixo:
```go
package main

import "fmt"

func main() {
    // Cria um canal sem capacidade definida
    ch := make(chan struct{})

    // Inicia uma Goroutine para enviar um valor para o canal
    go func() {
        ch <- struct{}{} // Envia um valor para o canal
    }()

    // Recebe um valor do canal e exibe uma mensagem
    <-ch
    fmt.Println("Valor recebido do canal")
}

```

## Select

O select é uma estrutura de controle em Go que permite que você monitore vários canais simultaneamente e execute a primeira operação que estiver pronta. Isso permite que você escreva código conciso e eficiente que responde a eventos de vários canais ao mesmo tempo.

```go
package main

import (
	"fmt"
	"time"
)

func enviarMesg(canal chan<- string, mensagem string, atraso time.Duration) {
	// Espera por um período de tempo aleatório
	time.Sleep(atraso)

	// Envia a mensagem para o canal
	canal <- mensagem
}

func main() {
	// Cria dois canais sem capacidade definida
	canal1 := make(chan string)
	canal2 := make(chan string)

	// Inicia duas Goroutines separadas para enviar mensagens para os canais
	go enviarMesg(canal1, "Mensagem para o canal 1", time.Second*2)
	go enviarMesg(canal2, "Mensagem para o canal 2", time.Second*1)

	// Use o select para receber a primeira mensagem que estiver pronta
	select {
	case msg1 := <-canal1:
		fmt.Println("Mensagem recebida do canal 1:", msg1)
	case msg2 := <-canal2:
		fmt.Println("Mensagem recebida do canal 2:", msg2)
	}
}

```

Neste exemplo, temos duas Goroutines diferentes que enviam mensagens para dois canais diferentes. Usando o select, estamos monitorando os dois canais e exibimos a primeira mensagem que chega em um deles.

Observe que estamos usando a sintaxe <- para receber valores dos canais dentro dos casos do select. O select aguardará até que um dos casos esteja pronto, o que significa que uma mensagem foi recebida de um dos canais.

Além disso, estamos usando o time.Sleep() na função enviarMsg() para simular um atraso aleatório antes de enviar a mensagem para o canal. Isso é feito para demonstrar que a mensagem enviada ao canal mais rapidamente pode não ser a primeira a ser recebida devido aos atrasos.

O select é uma ferramenta poderosa em Go que pode ser usada para escrever código eficiente que monitora vários canais ao mesmo tempo. É particularmente útil para lidar com eventos assíncronos e permitir que o programa execute várias tarefas simultaneamente.

Espero ter colaborado para melhor entendimento quando o assunto é Goroutine.

Um outro assunto complementar e importante é ["Workloads: CPU-Bound e IO-Bound"](https://dev.to/jeffotoni/workloads-em-go-59ab) recomendo a leitura para entender ainda mais.

Aqui você irá encontrar mais exemplos:
[manual Go em: gobootcamp.jeffotoni](https://gobootcamp.jeffotoni.com/#goroutines)
[Exemplos diversos de Goroutines em: goexample](https://github.com/jeffotoni/goexample/tree/master/goroutine)

Em **Go** teremos um arsenal para trabalhar com goroutines e gerenciar concorrências mas sem perder a simplicidade, legibilidade e produtividade.

***Go*** *é* ***amor*** ❤