Canais
1. Fechamento de canais
Quando um canal é fechado, qualquer tentativa subsequente de ler valores dele retorna o valor zero do tipo correspondente. Para tipos de referência, isso resulta em nil.
package main
import (
"fmt"
)
func main() {
channel := make(chan int)
go process(channel)
for {
value, isOpen := <-channel
if !isOpen {
fmt.Println("Canal foi fechado")
break
}
fmt.Println("Valor lido:", value)
}
fmt.Println("Valor lido após fechamento:", <-channel)
for val := range channel {
fmt.Println("Valor lido:", val)
}
}
func process(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
2. Canais com buffer
Os canais com buffer têm várias aplicações importantes na linguagem Go, principalmente em comunicação assíncrona, controle de fluxo e coordenação entre goroutines. Algumas das principais utilidades incluem:
- Comunicação assíncrona: Permitem que operações de envio e recepção ocorram em momentos distintos. O envio coloca dados no canal, enquanto o recebimento processa esses dados posteriormente. Isso é útil para desacoplar produtores e consumidores com diferentes velocidades.
- Controle de fluxo: Podem limitar o volume de dados em trânsito no canal. Quando cheio, operações de envio bloqueiam até que espaço esteja disponível, ajudando a gerenciar a taxa de entrada de dados.
- Coordenação de tarefas: Facilitam a distribuição de trabalho entre múltiplas goroutines. Ao armazenar tarefas ou mansagens em buffer, podem implementar pools de trabalhadores ou distribuição concorrente de tarefas.
- Desacoplamento de operações: Permite que envios e recepções sejam feitos em goroutines separadas, aumentando a concorrência do programa e melhorando a manutenibilidade do código.
- Prevenção de vazamentos de goroutine: Em canais sem buffer, envio e recepção devem estar prontos simultaneamente. Isso pode causar bloqueios e vazamentos. Canais com buffer evitam isso, pois o envio pode continuar mesmo sem receptores imediatos.
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
ch1 := make(chan int)
fmt.Println(len(ch1), cap(ch1))
ch2 := make(chan int, 5)
fmt.Println(len(ch2), cap(ch2))
ch2 <- 1
ch2 <- 1
ch2 <- 1
ch2 <- 1
ch2 <- 1
fmt.Println(len(ch2), cap(ch2))
//ch2 <- 1 Tentar inserir um sexto elemento causará deadlock.
//Armazena quatro elementos antes de ler
ch3 := make(chan string, 4)
go producer(ch3)
for val := range ch3 {
time.Sleep(time.Second)
fmt.Println(val)
}
}
func producer(ch chan string) {
for i := 0; i < 10; i++ {
ch <- "teste" + strconv.Itoa(i)
fmt.Println("Dado escrito:", "teste"+strconv.Itoa(i))
}
close(ch)
}
3. Canais direcionados (unidirecionais)
Canais que permitem apenas envio ou apenas recepção de dados.
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int)
go sender(ch1)
go receiver(ch1)
time.Sleep(time.Second)
}
func sender(ch chan<- int) {
ch <- 100
}
func receiver(ch <-chan int) {
data := <-ch
fmt.Println(data)
}