DEV Community

Cover image for Go Concurrency: Como rodar múltiplas coisas ao mesmo tempo sem pirar
Rafael Pazini
Rafael Pazini

Posted on

3 1 1

Go Concurrency: Como rodar múltiplas coisas ao mesmo tempo sem pirar

Go foi projetado desde o início para facilitar a concorrência, tornando possível escrever código que executa várias tarefas ao mesmo tempo sem perder a cabeça. Neste artigo, vou te mostrar o que é concorrência, como funciona em Go e como você pode usar goroutines e channels para deixar seu código mais eficiente e responsivo.


Concorrência vs. Paralelismo: Qual é a Diferença?

Antes de tudo, vamos esclarecer um ponto: concorrência não é a mesma coisa que paralelismo.

  • Concorrência: quando seu código lida com múltiplas tarefas ao mesmo tempo, alternando entre elas conforme necessário.
  • Paralelismo: quando seu código executa várias tarefas ao mesmo tempo, literalmente ao mesmo tempo, geralmente em múltiplos núcleos de CPU.

Pensa assim: você está cozinhando um jantar. Enquanto a água ferve (uma tarefa), você pica os legumes (outra tarefa). Isso é concorrência. Agora, se você tivesse um clone seu picando os legumes enquanto você ferve a água, isso seria paralelismo.


Goroutines: Executando Funções Concorrentemente

Em Go, criar uma função concorrente é incrivelmente fácil. Basta colocar go antes da chamada da função e pronto, ela roda em segundo plano sem te incomodar.

Exemplo simples de goroutine:

package main

import (
    "fmt"
    "time"
)

func digaOla() {
    fmt.Println("Olá, mundo!")
}

func main() {
    go digaOla() // Rodando a função como goroutine
    time.Sleep(time.Second) // Esperando um pouco para ver a saída
}
Enter fullscreen mode Exit fullscreen mode

Aqui, digaOla() é executada de forma concorrente, mas como o programa principal pode terminar antes que ela rode, colocamos um time.Sleep(time.Second) só pra garantir que dê tempo de ver a saída. Claro que, em código real, a gente usaria algo mais robusto, como channels, para sincronização.


Channels: Conversando Entre Goroutines

Goroutines são legais, mas como elas conversam entre si? Em Go, usamos channels, que funcionam como uma esteira de produção: uma goroutine coloca um dado de um lado e outra goroutine pega do outro.

Unbuffered Channels: Mensagens "Na Hora"

Os unbuffered channels são como passar um recado para um amigo pessoalmente: você fala e ele escuta na mesma hora.

package main

import "fmt"

func main() {
    mensagens := make(chan string) // Criando um channel de strings

    go func() {
        mensagens <- "Olá, Go!"
    }()

    fmt.Println(<-mensagens) // Recebendo a mensagem do channel
}
Enter fullscreen mode Exit fullscreen mode

Aqui, a goroutine anônima envia uma mensagem pelo channel, e a main recebe e imprime. O channel bloqueia até que haja um receptor do outro lado, garantindo sincronização.

Buffered Channels: Guardando Mensagens

Buffered channels são como um correio: você pode colocar mensagens na caixa antes que o carteiro as retire.

package main

import "fmt"

func main() {
    numeros := make(chan int, 3) // Criando um buffered channel com capacidade 3

    numeros <- 1
    numeros <- 2
    numeros <- 3

    fmt.Println(<-numeros) // 1
    fmt.Println(<-numeros) // 2
    fmt.Println(<-numeros) // 3
}
Enter fullscreen mode Exit fullscreen mode

Aqui, o channel pode armazenar até 3 valores sem bloquear. Se tentarmos enviar um 4º valor antes de consumir algum, o programa trava até que haja espaço.


Select: Lidando com Múltiplos Channels

O comando select em Go permite ouvir vários channels ao mesmo tempo, tipo um garçom atendendo várias mesas ao mesmo tempo.

package main

import (
    "fmt"
    "time"
)

func main() {
    canal1 := make(chan string)
    canal2 := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        canal1 <- "Mensagem do canal 1"
    }()

    go func() {
        time.Sleep(1 * time.Second)
        canal2 <- "Mensagem do canal 2"
    }()

    select {
    case msg1 := <-canal1:
        fmt.Println(msg1)
    case msg2 := <-canal2:
        fmt.Println(msg2)
    }
}
Enter fullscreen mode Exit fullscreen mode

Aqui, select escolhe o primeiro channel que estiver pronto, garantindo que a resposta mais rápida seja processada primeiro.


Concorrência na Vida Real

Agora que você já viu como usar goroutines e channels, aqui estão alguns exemplos do mundo real onde a concorrência brilha:

  1. Servidores Web: Cada requisição é tratada em uma goroutine separada, evitando travamentos.
  2. Scrapers de Dados: Se você precisar buscar informações de vários sites, pode fazer isso concorrentemente e acelerar o processo.
  3. Jogos Multiplayer: Goroutines podem gerenciar o estado de cada jogador e enviar atualizações rapidamente.

Conclusão

Go torna a concorrência fácil e eficiente, permitindo criar aplicações escaláveis e responsivas sem dores de cabeça. Ao dominar goroutines, channels e select, você consegue construir sistemas que aproveitam melhor o hardware e evitam travamentos.

Então, da próxima vez que precisar rodar várias coisas ao mesmo tempo, lembre-se: goroutines são seus amigos, e channels são a melhor forma de manter a conversa em ordem! 🚀

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more