Menu
Coddy logo textTech

Canais e Comunicação

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 60 de 107.

Canais são o mecanismo principal do Go para comunicação entre goroutines. Em vez de compartilhar memória diretamente, as goroutines enviam e recebem valores através de canais, seguindo a filosofia do Go: "Não se comunique compartilhando memória; compartilhe memória se comunicando."

Crie um canal usando a função make com a palavra-chave chan:

messages := make(chan string)

Use o operador de seta <- para enviar e receber valores. A seta aponta na direção do fluxo de dados:

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "hello"  // enviar para o canal
    }()
    
    msg := <-messages  // receber do canal
    fmt.Println(msg)   // Saída: hello
}

Por padrão, envios e recebimentos bloqueiam até que o outro lado esteja pronto. Quando a goroutine envia "hello", ela espera até que main esteja pronta para receber. Este comportamento de bloqueio fornece sincronização integrada—sem a necessidade de time.Sleep.

Você pode fechar um canal para sinalizar que não serão enviados mais valores. Os receptores podem detectar isso:

close(messages)

msg, ok := <-messages
if !ok {
    fmt.Println("Channel closed")
}

Canais são tipados—um chan string só pode carregar strings. Essa segurança de tipo garante que as goroutines se comuniquem com os tipos de dados corretos, capturando erros em tempo de compilação em vez de tempo de execução.

challenge icon

Desafio

Fácil

Vamos construir um sistema de retransmissão de mensagens que demonstra como as goroutines se comunicam através de canais. Você criará um pipeline onde as mensagens fluem entre diferentes estágios de processamento, com cada estágio sendo executado de forma concorrente.

Você organizará seu código em dois arquivos:

  • relay.go: Defina seu pipeline de processamento de mensagens.

    Crie uma struct Message com os campos ID (int) e Content (string).

    Implemente três funções que representam diferentes estágios do seu pipeline:

    • Producer(messages []Message, out chan Message) - Recebe um slice de mensagens e envia cada uma para o canal de saída. Após enviar todas as mensagens, feche o canal para sinalizar a conclusão.
    • Transformer(in chan Message, out chan Message) - Recebe mensagens do canal de entrada, transforma o conteúdo de cada mensagem para letras maiúsculas e envia a mensagem transformada para o canal de saída. Quando o canal de entrada for fechado (detectado usando a forma de recebimento de dois valores), feche o canal de saída.
    • Consumer(in chan Message) []string - Recebe todas as mensagens do canal de entrada e as coleta em um slice de strings formatadas. Cada string deve seguir o formato: Message [ID]: [Content]. Retorne o slice quando o canal for fechado.
  • main.go: Configure o pipeline e orquestre o fluxo concorrente de mensagens.

    Leia o número de mensagens, depois leia o ID e o conteúdo de cada mensagem. Crie as mensagens e configure dois canais para conectar seus três estágios do pipeline. Inicie o Producer e o Transformer como goroutines e, em seguida, execute o Consumer na goroutine principal para coletar os resultados. Imprima cada resultado em uma linha separada.

As seguintes entradas serão fornecidas:

  • Linha 1: Número de mensagens (inteiro)
  • Linhas seguintes: Para cada mensagem, duas linhas - o ID da mensagem (inteiro) e, em seguida, seu conteúdo (string)

Por exemplo, dado:

2
1
hello world
2
go channels

Sua saída deve ser:

Message 1: HELLO WORLD
Message 2: GO CHANNELS

O pipeline flui assim: Producer envia mensagens → Transformer converte para maiúsculas → Consumer coleta e formata os resultados. Cada seta representa um canal conectando estágios concorrentes. Use o pacote strings para a transformação em maiúsculas.

Folha de consulta

Canais são o principal mecanismo do Go para comunicação entre goroutines. Crie um canal usando make com a palavra-chave chan:

messages := make(chan string)

Use o operador de seta <- para enviar e receber valores. A seta aponta na direção do fluxo de dados:

messages <- "hello"  // enviar para o canal
msg := <-messages    // receber do canal

Por padrão, envios e recebimentos bloqueiam até que o outro lado esteja pronto, fornecendo sincronização integrada:

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "hello"  // enviar para o canal
    }()
    
    msg := <-messages  // receber do canal
    fmt.Println(msg)   // Saída: hello
}

Feche um canal para sinalizar que não serão enviados mais valores. Os receptores podem detectar o fechamento usando a forma de recebimento de dois valores:

close(messages)

msg, ok := <-messages
if !ok {
    fmt.Println("Channel closed")
}

Canais são tipados—um chan string só pode carregar strings, fornecendo segurança de tipo em tempo de compilação.

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Ler o número de mensagens
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// Ler as mensagens
	messages := make([]Message, n)
	for i := 0; i < n; i++ {
		idLine, _ := reader.ReadString('\n')
		id, _ := strconv.Atoi(strings.TrimSpace(idLine))
		contentLine, _ := reader.ReadString('\n')
		content := strings.TrimSpace(contentLine)
		messages[i] = Message{ID: id, Content: content}
	}

	// TODO: Criar dois canais para conectar os estágios do pipeline

	// TODO: Iniciar o Producer como uma goroutine

	// TODO: Iniciar o Transformer como uma goroutine

	// TODO: Executar o Consumer na goroutine principal e coletar os resultados

	// TODO: Imprimir cada resultado em uma linha separada
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos