Buffered channels
Se você já conseguiu entender e praticar bem a parte de channels, podemos falar sobre buffered channels.
Buffered channels são channels que não bloqueiam o envio e recebimento em alguns cenários, porque eles guardam os valores enviados temporariamente num buffer, que vai ser lido e removido do buffer quando alguém receber.
Você cria um buffered channel com o tamanho do buffer que você quer, por exemplo 5, daí você vai poder fazer 5 envios sem bloquear a goroutine.
A partir daí, enquanto o buffer estiver cheio, os próximos envios vão bloquear, da mesma forma que num channel sem buffer.
// basta passar um tamanho na hora de criar o channel
ch := make(chan string, 3)
// não vão bloquear a gourotine
ch <- "A"
ch <- "B"
ch <- "C"
// buffer cheio, novos envios vão bloquear a goroutine
// até que surja um espaço livre no buffer
ch <- "D"
Da mesma forma, receber de um buffered channel não vai bloquear se o buffer não estiver vazio.
Se ele estiver vazio, vai funcionar como um channel sem buffer.
ch := make(chan string, 3)
// não vão bloquear a gourotine
ch <- "A"
ch <- "B"
ch <- "C"
// não vão bloquear a goroutine
<-ch // "A"
<-ch // "B"
<-ch // "C"
// buffer vazio, vai bloquear a goroutine até um novo envio
<-ch
Usando buffered channels para controlar número de goroutines
Uma grande vantagem dos buffered channels é que eles passam a bloquear quando o buffer está cheio, e podemos usar isso ao nosso favor.
Nesse exemplo aqui eu tenho um programa que vai ficar criando goroutines indeterminadamente e cada goroutine faz uma requisição GET.
package main
import (
"fmt"
"net/http"
)
func main() {
for {
go func() {
client := http.Client{}
resp, _ := client.Get("https://www.google.com")
resp.Body.Close()
fmt.Println(resp.Status)
}()
}
}
O problema é que não podemos ficar criando goroutines e clientes HTTP sem limites, porque memória não é infinita, e nem vai ser muito eficiente fazer milhares de requisições ao mesmo tempo.
Esse programa vai cedo ou tarde dar um erro e vai encerrar:
"runtime error: invalid memory address or nil pointer dereference"
Stack:
4 0x0000000000744764 in main.main.func1
at /home/igor/Git/pessoal/got/main.go:21
Para resolver isso, vamos criar um buffered channel antes do loop:
// o tipo não importa muito, porque não estamos interessados no valor enviado
// nesse caso vou limitar para 50 goroutines fazendo requisições
limitter := make(chan bool, 50)
Toda vez antes de criar uma goroutine vamos adicionar um valor ao buffer:
for {
limitter <- true
go func() {
...
}()
}
Toda vez que a goroutine finalizar, vamos tirar um valor do buffer:
for {
limitter <- true
go func() {
...
<-limitter
}()
}
O resultado vai ser:
package main
import (
"fmt"
"net/http"
)
func main() {
finished := make(chan bool, 50)
for {
finished <- true
go func() {
client := http.Client{}
resp, _ := client.Get("https://www.google.com")
resp.Body.Close()
fmt.Println(resp.Status)
<-finished
}()
}
}
Top comments (0)