Aula 25 - Tutorial Golang - Goroutines
Goroutines
O que são
Goroutines?
Goroutines são funções ou métodos que são executados simultaneamente com outras funções ou métodos.
Goroutines podem ser consideradas
threads leves.
Em
Go, todo programa tem pelo menos uma
goroutine: a
goroutine main.
O custo de criar uma
Goroutine é pequeno quando comparado a uma
thread.
Portanto, é comum que os aplicativos
Go tenham milhares de
Goroutines sendo
executadas simultaneamente.
Vantagens de Goroutines sobre as threads
Goroutines são extremamente baratas quando comparadas as
threads.
Elas têm apenas alguns
kb de tamanho na pilha, e a pilha pode crescer e diminuir de acordo com as necessidades da aplicação, enquanto no caso de
threads o tamanho da pilha deve ser
especificado e é
fixo.
As
Goroutines são multiplexadas para um número menor de
threads no
SO.
Pode haver apenas uma
thread em um programa com milhares de
Goroutines.
Se qualquer
Goroutine nesse bloco de
thread disser que está aguardando entrada do usuário, outra
thread no
sistema operacional, será criada, e as
Goroutines restantes serão movidas para a nova thread do
SO.
Tudo isso é cuidado em tempo de execução e nós, como programadores, somos abstraídos desses detalhes intrincecos e recebemos uma API limpa para trabalhar com simultaneidade.
Goroutines se comunicam usando canais.
Os
canais, por design, evitam que as condições de corrida aconteçam ao acessar a memória compartilhada usando
Goroutines.
Os canais podem ser pensados como um
tubo através do qual as
Goroutines se comunicam.
Como iniciar uma Goroutine?
Prefixe a chamada de função ou método com a palavra-chave
go e você terá uma nova
Goroutine rodando simultaneamente.
Vamos criar uma Goroutine? :)
package main
import (
"fmt"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
O
go hello() logo no início da
main(), inicia uma nova
Goroutine.
Agora a função
hello() será executada simultaneamente com a função
main().
A função
main() é executada em sua própria Goroutine e é chamada de
Goroutine principal.
Execute este programa e você terá uma surpresa!
Este programa produz apenas o texto informando:
função principal.
Saída:
main function
O que aconteceu com a Goroutine que começamos?
Precisamos entender as duas principais propriedades das
Goroutines para entender por que isso acontece.
Quando uma nova Goroutine é iniciada, a chamada da
Goroutine retorna imediatamente.
Ao contrário das funções, o controle não espera que a
Goroutine termine de executar.
O controle retorna imediatamente para a próxima linha de código após a chamada de
Goroutine e quaisquer valores de retorno do
Goroutine são ignorados.
A
Goroutine principal deve estar em execução para que qualquer outra
Goroutine seja executada.
Se a
Goroutine principal for encerrada, o programa será encerrado e nenhuma outra
Goroutine será executada.
Acho que agora você será capaz de entender por que nossa
Goroutine não funcionou.
Após a chamada para
go hello(), o controle retornou imediatamente para a próxima linha de código sem esperar que o
hello goroutine termine e imprimiu a função
main().
Em seguida, a
Goroutine principal foi encerrada, pois não há outro código para executar e, portanto, a Goroutine
hello() não teve a chance de ser executada.
Vamos corrigir isso agora!
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
Na parte do código onde tem:
time.Sleep(1 * time.Second), chamamos o método
Sleep do pacote
time, que faz a rotina go na qual ele está sendo executada, dormir(
sleep).
Neste caso, a
goroutine principal é adormecida por 1 segundo.
Agora a chamada para
go hello(), tem tempo suficiente para ser executada antes que a
Goroutine principal termine.
Este programa primeiro imprime a
goroutine Hello world, espera 1 segundo e depois imprime a função main.
Essa maneira de usar o
Sleep na Goroutine principal para esperar que outras
Sleep terminem sua execução, é um hack que estamos usando para entender como as
Goroutines funcionam.
Vamos ver mais um exemplo:
package main
import (
"fmt"
)
func main() {
hello("Martin")
hello("Lucia")
hello("Michal")
hello("Jozef")
hello("Peter")
}
func hello(name string) {
fmt.Printf("Hello %s!\n", name)
}
Nesse código acima, temos a chamada normal, síncrona, da função
hello(), agora vamos ver o mesmo código com
Gourotine.
package main
import (
"fmt"
)
func hello(name string) {
fmt.Printf("Hello %s!\n", name)
}
func main() {
go hello("Martin")
go hello("Lucia")
go hello("Michal")
go hello("Jozef")
go hello("Peter")
}
Veja que não houve tempo de imprimir, porque a
Goroutine principal finalizou muito rápido, sem esperar a execução das chamadas da função
hello().
Para fazer a
Goroutine principal esperar, ao invés de usar o recurso do
Sleep como fizemos antes, vamos adicionar
fmt.Scanln() para o programa ficar esperando alguma entrada do usuário para encerrar a execução.
package main
import (
"fmt"
)
func hello(name string) {
fmt.Printf("Hello %s!\n", name)
}
func main() {
go hello("Martin")
go hello("Lucia")
go hello("Michal")
go hello("Jozef")
go hello("Peter")
fmt.Scanln()
}
Canais
Os
canais podem ser usados para bloquear a
Goroutine principal até que todas as outras
Goroutines terminem sua execução.
E é justamente canais, que veremos na próxima aula.
É isso pessoal, fico por aqui!
Até mais. :)
Meus links de afiliados:
Obrigado e bons estudos. ;)