Guia Prático de Concorrência em Go

Planejamento de Tarefas

sync.WaitGroup

package main

import (
	"fmt"
	"sync"
)

// Exemplo de uso de goroutines
func imprimirMensagem() {
	fmt.Println("Goroutine executada")
}

var grupo sync.WaitGroup

func tarefaWaitGroup(id int) {
	defer grupo.Done()
	fmt.Println(id, "tarefa concluída")
}

func main() {
	go imprimirMensagem()
	for i := 0; i < 10; i++ {
		grupo.Add(1)
		go tarefaWaitGroup(i)
	}
	grupo.Wait()
	fmt.Println("Final do programa")
}

Saída:
Goroutine executada
0 tarefa concluída
1 tarefa concluída
2 tarefa concluída
3 tarefa concluída
...
9 tarefa concluída
Final do programa

sync.Once

package main

import (
	"fmt"
	"sync"
)

var umaVez sync.Once

func inicializar() {
	fmt.Println("Inicialização executada uma vez")
}

func operacaoUnica() {
	umaVez.Do(func() {
		inicializar()
	})
}

func main() {
	operacaoUnica()
	operacaoUnica()
}

Saída:
Inicialização executada uma vez

sync.Cond

package main

import (
	"fmt"
	"sync"
	"time"
)

var pronto bool

func produtor(cond *sync.Cond) {
	fmt.Println("Produtor iniciado")
	time.Sleep(50 * time.Millisecond)
	cond.L.Lock()
	time.Sleep(100 * time.Millisecond)
	pronto = true
	fmt.Println("Produtor finalizado")
	cond.L.Unlock()
	cond.Broadcast()
	fmt.Println("Notificando consumidores")
}

func consumidor(nome string, cond *sync.Cond) {
	fmt.Printf("Consumidor %s iniciado\n", nome)
	cond.L.Lock()
	for !pronto {
		fmt.Printf("Consumidor %s aguardando\n", nome)
		cond.Wait()
	}
	fmt.Printf("Consumidor %s trabalhando\n", nome)
	time.Sleep(50 * time.Millisecond)
	fmt.Printf("Consumidor %s finalizado\n", nome)
	cond.L.Unlock()
}

func main() {
	cond := sync.NewCond(&sync.Mutex{})
	tarefas := []string{"A", "B"}
	for _, t := range tarefas {
		go consumidor(t, cond)
	}
	go produtor(cond)
	time.Sleep(2 * time.Second)
	fmt.Println("Fim do programa")
}

Saída:
Produtor iniciado
Consumidor A iniciado
Consumidor B iniciado
Consumidor A aguardando
Consumidor B aguardando
Produtor finalizado
Notificando consumidores
Consumidor A trabalhando
Consumidor A finalizado
Consumidor B trabalhando
Consumidor B finalizado
Fim do programa

runtime

package main

import (
	"fmt"
	"runtime"
	"time"
)

func lavarRoupas(iter int) {
	for i := 0; i < iter; i++ {
		if i == 2 {
			defer fmt.Println("Lavar roupas finalizado abruptamente")
			runtime.Goexit()
		}
		fmt.Println("Lavando:", i)
		runtime.Gosched()
	}
}

func cozinhar(iter int) {
	for i := 0; i < iter; i++ {
		if i == 2 {
			defer fmt.Println("Cozinhar finalizado abruptamente")
			runtime.Goexit()
		}
		fmt.Println("Cozinhando:", i)
		runtime.Gosched()
	}
}

func main() {
	runtime.GOMAXPROCS(1)
	go lavarRoupas(5)
	go cozinhar(5)
	time.Sleep(3 * time.Second)
	fmt.Println("Todas as tarefas concluídas")
}

Saída:
Cozinhando: 0
Lavando: 0
Cozinhando: 1
Lavando: 1
Cozinhar finalizado abruptamente
Lavar roupas finalizado abruptamente
Todas as tarefas concluídas

Canais

package main

import (
	"fmt"
	"time"
)

func esperarTarefa(canal <-chan int) {
	fmt.Println("Esperando")
	<-canal
	fmt.Println("Recebeu sinal")
}

func executarTarefa(canal chan int) {
	defer close(canal)
	fmt.Println("Executando")
	time.Sleep(100 * time.Millisecond)
	fmt.Println("Executou")
}

func main() {
	canal := make(chan int)
	go esperarTarefa(canal)
	go esperarTarefa(canal)
	go executarTarefa(canal)
	time.Sleep(2 * time.Second)
}

Saída:
Executando
Esperando
Esperando
Executou
Recebeu sinal
Recebeu sinal

Comunicação de Dados

Canais com Buffer

package main

import (
	"fmt"
	"time"
)

func main() {
	canal := make(chan int, 5)
	go func() {
		for i := 0; i < 5; i++ {
			fmt.Println("Enviando:", i)
			canal <- i
		}
		close(canal)
	}()

	go func() {
		for v := range canal {
			fmt.Println("ReceptorA:", v)
		}
	}()

	go func() {
		for v := range canal {
			fmt.Println("ReceptorB:", v)
		}
	}()

	time.Sleep(1 * time.Second)
}

Saída:
Enviando: 0
Enviando: 1
Enviando: 2
Enviando: 3
Enviando: 4
ReceptorA: 0
ReceptorB: 1
ReceptorA: 2
ReceptorB: 3
ReceptorA: 4

Canais com Select

package main

import (
	"fmt"
	"time"
)

func main() {
	canal1 := make(chan int, 1)
	canal2 := make(chan int, 1)

	go func() {
		time.Sleep(200 * time.Millisecond)
		canal1 <- 11
		close(canal1)
	}()

	go func() {
		time.Sleep(500 * time.Millisecond)
		canal2 <- 111
		close(canal2)
	}()

	for i := 0; i < 10; i++ {
		select {
		case d, ok := <-canal1:
			if ok {
				fmt.Println("Canal1:", d)
			} else {
				fmt.Println("Canal1 fechado")
			}
		case d, ok := <-canal2:
			if ok {
				fmt.Println("Canal2:", d)
			} else {
				fmt.Println("Canal2 fechado")
			}
		default:
			fmt.Println("Default")
		}
		time.Sleep(200 * time.Millisecond)
	}
}

Saída:
Default
Default
Canal1: 11
Canal1 fechado
Canal2: 111
Canal1 fechado
Canal2 fechado
Canal2 fechado
Canal2 fechado
Canal1 fechado

sync.Mutex

package main

import (
	"fmt"
	"sync"
)

var total int

func main() {
	lock := sync.Mutex{}
	wg := sync.WaitGroup{}

	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			lock.Lock()
			total++
			lock.Unlock()
		}()
	}
	wg.Wait()
	fmt.Println("Total:", total)
}

Saída:
Total: 100

atomic

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var contagem int64

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			atomic.AddInt64(&contagem, 1)
		}()
	}
	wg.Wait()
	fmt.Println("Contagem:", contagem)
}

Saída:
Contagem: 100

sync.Map

package main

import (
	"fmt"
	"sync"
	"time"
)

var mapaConcorrente sync.Map
var wg sync.WaitGroup

func main() {
	// Operações básicas
	mapaConcorrente.Store("chave", "valor")
	v, ok := mapaConcorrente.Load("chave")
	fmt.Printf("Load: chave=%v, valor=%v, ok=%v\n", "chave", v, ok)

	novaChave := "idade"
	novoValor := "25"
	v, carregou := mapaConcorrente.LoadOrStore(novaChave, novoValor)
	fmt.Printf("LoadOrStore: chave=%v, valor=%v, carregou=%v\n", novaChave, v, carregou)

	mapaConcorrente.Delete(novaChave)
	v, ok = mapaConcorrente.LoadAndDelete("chave")
	fmt.Printf("LoadAndDelete: chave=%v, valor=%v\n", "chave", v)

	for i := 0; i < 5; i++ {
		mapaConcorrente.Store(i, i*10)
	}
	mapaConcorrente.Range(func(k, v interface{}) bool {
		fmt.Printf("Range: chave=%v, valor=%v\n", k, v)
		return true
	})

	// Concorrência
	wg.Add(3)
	go func() {
		defer wg.Done()
		for i := 0; i < 3; i++ {
			mapaConcorrente.Store(i*100, i)
		}
	}()
	time.Sleep(100 * time.Millisecond)

	go func() {
		defer wg.Done()
		mapaConcorrente.Range(func(k, v interface{}) bool {
			fmt.Println("LeitorA:", k, v)
			return true
		})
	}()

	go func() {
		defer wg.Done()
		mapaConcorrente.Range(func(k, v interface{}) bool {
			fmt.Println("LeitorB:", k, v)
			return true
		})
	}()

	wg.Wait()
}

Saída:
Load: chave=chave, valor=valor, ok=true
LoadOrStore: chave=idade, valor=25, carregou=false
LoadAndDelete: chave=chave, valor=valor
Range: chave=0, valor=0
Range: chave=1, valor=10
Range: chave=2, valor=20
Range: chave=3, valor=30
Range: chave=4, valor=40
LeitorA: 0 0
LeitorB: 0 0
LeitorA: 1 10
LeitorB: 1 10
...

sync.Pool

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var criados int32
var grupo sync.WaitGroup

func main() {
	pool := &sync.Pool{
		New: func() interface{} {
			atomic.AddInt32(&criados, 1)
			buf := make([]byte, 16)
			return &buf
		},
	}
	numWorkers := 5
	grupo.Add(numWorkers)
	for i := 0; i < numWorkers; i++ {
		go func() {
			defer grupo.Done()
			buf := pool.Get()
			defer pool.Put(buf)
		}()
	}
	grupo.Wait()
	fmt.Println("Objetos criados:", criados)
}

Saída:
Objetos criados: 3

Tags: go goroutines channels sync.WaitGroup sync.Once

Publicado em 6-2 05:03 por Thomas