Conceitos Avançados em Go: Canais

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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)
}

Tags: go canal Golang goroutine programacao-concorrente

Publicado em 9-26 17:43