Aula 29 – Tutorial Golang – Select
Aula 29 – Tutorial Golang – Select
Página principal do blog
Todas as aulas desse curso
Aula 28 Aula 30
Redes Sociais:
Meus links de afiliados:
Hostinger
Digital Ocean
One.com
Melhore seu NETWORKING
https://digitalinnovation.one/
Participe de comunidades de desenvolvedores:
Fiquem a vontade para me adicionar ao linkedin.
E também para me seguir no https://github.com/toticavalcanti.
Código final da aula:
https://github.com/toticavalcanti
Canais do Youtube:
Toti
Backing Track / Play-Along
Código Fluente
Putz!
Vocal Techniques and Exercises
PIX para doações
Aula 29 – Tutorial Golang – Select
Fontes usadas para esse post:
Select
A instrução select permite que uma goroutine aguarde várias operações de comunicação.
Um select bloqueia até que um de seus cases possa ser executado, então ele executa esse case.
Se vários cases estiverem prontos para serem executados, um deles é escolhido aleatoriamente.
A sintaxe é semelhante a switch, o que muda é que cada uma das instruções case é uma operação de canal.
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
// sleeps for 6 seconds then writes the
// text "from server1" to the channel ch
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
// sleeps for 3 seconds then writes the
// text "from server2" to the channel ch
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
// calls the go Goroutines server1
go server1(output1)
// calls the go Goroutines server2
go server2(output2)
// select statement blocks until one
// of its cases is ready
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
Saída:
from server2
Uso prático do select
A razão para ter chamado as funções de server1() e server2(), é para ilustrar o uso prático do select .
Vamos supor que temos um aplicativo de missão crítica e precisamos retornar a saída para o usuário o mais rápido possível.
O banco de dados do aplicativo é replicado e armazenado em diferentes servidores na nuvem, mundo afora.
Suponha que as funções server1() e server2(), estejam de fato se comunicando com 2 desses servidores.
O tempo de resposta de cada servidor depende da carga de cada um e do atraso da rede.
Enviamos a solicitação para ambos os servidores e aguardamos nos canais correspondentes a resposta usando a instrução select.
O servidor que responder primeiro é escolhido pelo select e a outra resposta é ignorada.
Desta forma, podemos enviar a mesma solicitação para vários servidores e retornar a resposta mais rápida ao usuário :).
Default case – Caso Padrão
O caso padrão em uma instrução select, é executado quando nenhum dos outros casos está pronto.
Isso geralmente é usado para impedir que a instrução select bloqueie.
Vamos a um exemplo.
package main
import (
"fmt"
"time"
)
func process(ch chan string) {
// sleeps for 10.5 seconds then writes the
// text "process successful" to the channel ch
time.Sleep(10500 * time.Millisecond)
ch <- "process successful"
}
func main() {
ch := make(chan string)
go process(ch)
//infinite loop
for {
// sleeps for 1000 milliseconds (1 second)
// during the start of each iteration
time.Sleep(1000 * time.Millisecond)
// and then performs a select operation
select {
case v := <-ch:
// will be executed after 10.5 seconds when func
// process will write in channel "process successful"
fmt.Println("received value: ", v)
return
default:
fmt.Println("no value received")
}
}
}
Saída:
no value received
no value received
no value received
no value received
no value received
no value received
no value received
no value received
no value received
no value received
received value: process successful
No programa acima, a função process() dorme por 10.500 milissegundos (10,5 segundos).
Depois escreve “process successful” no canal ch.
Esta função é chamada na main() em uma goroutine.
Em seguida temos um loop for infinito, iniciado na goroutine principal.
O loop infinito dorme por 1000 milissegundos (1 segundo) durante o início de cada iteração e em seguida, executa uma operação de seleção.
Durante os primeiros 10.500 milissegundos, o primeiro caso da instrução select case v := <-ch:, não estará pronto, pois a goroutine process escreverá no canal ch só após 10.5 segundos.
Portanto, o default será executado durante esse tempo e o programa imprimirá no value received 10 vezes.
Após 10,5 segundos, a goroutine process escreve process successful no ch.
Agora, o primeiro caso da instrução select será executado e o programa imprimirá received value: process successful, e em seguida, encerra.
Deadlock e Default Case
No programa abaixo, criamos um canal ch.
Tentamos ler deste canal dentro do select.
A instrução select será bloqueada para sempre, pois nenhuma outra goroutine está escrevendo nesse canal e portanto, resultará em um impasse (deadlock).
package main
func main() {
ch := make(chan string)
select {
case <-ch:
}
}
Este programa entrará em pânico no tempo de execução com a seguinte mensagem.
Saída:
fatal error: all goroutines are asleep – deadlock!
goroutine 1 [chan receive]:
main.main()
C:/Users/user01/projects/go/learning_golang/28 – select/select.go:6 +0x2c
exit status 2
Se um default case estivesse presente, esse deadlock não aconteceria, pois o default case será executado quando nenhum outro case estiver pronto.
Veja logo abaixo, o programa acima reescrito com um default case.
package main
import "fmt"
func main() {
ch := make(chan string)
select {
case <-ch:
default:
fmt.Println("default case executed")
}
}
Saída:
default case executed
Seleção Aleatória
Quando vários cases em uma instrução select estiverem prontos, um deles será executado aleatoriamente.
Se você executar este programa várias vezes, a saída variará entre “from server1” ou “from server2“, dependendo de qual case for escolhido aleatoriamente.
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
ch <- "from server1"
}
func server2(ch chan string) {
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
time.Sleep(1 * time.Second)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
Gotcha – Select Vazio
package main
func main() {
select {}
}
A instrução select é bloqueada até que um de seus cases seja executado.
Nesse caso, a instrução select não possui nenhum case e portanto, será bloqueada para sempre, resultando em um impasse (deadlock).
Este programa entrará em pânico com a seguinte saída.
Saída:
fatal error: all goroutines are asleep – deadlock!
goroutine 1 [select (no cases)]:
main.main()
C:/Users/user01/projects/go/learning_golang/28 – select/select.go:4 +0x17
Eu fico por aqui e agradeço a você pela audiência.
Até mais. 🙂
Página principal do blog
Todas as aulas desse curso
Aula 28 Aula 30
Meus links de afiliados:
Hostinger
Digital Ocean
One.com
Obrigado e bons estudos. 😉