Skip to content

once

sync.Once é uma ferramenta de sincronização da biblioteca padrão do Go, usada para garantir que uma função seja executada apenas uma vez em ambiente concorrente. É comumente usado em cenários como inicialização tardia, inicialização de recursos globais e outros, garantindo que uma operação específica seja executada apenas uma vez, mesmo que múltiplas goroutines executem concorrentemente.

go
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	var once sync.Once
	for i := range 10 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			once.Do(func() {
				fmt.Println(i)
			})
		}()
	}
	wg.Wait()
}

No código acima, há um total de 10 goroutines, mas independentemente disso, apenas uma goroutine executará o fmt.Println(i) dentro de once.Do(). Qual goroutine imprimirá depende de qual goroutine chegar primeiro ao once.Do().

Estrutura

go
type Once struct {
    done atomic.Uint32
    m    Mutex
}

Sua estrutura interna é muito simples:

  1. done, um valor atômico usado para indicar se já foi executado
  2. m, mutex de exclusão mútua, usado para bloquear outras goroutines que desejam executar

Seu princípio é travar antes de executar, depois atualizar done, e destravar após a execução.

Do

go
func (o *Once) Do(f func()) {
	if o.done.Load() == 0 {
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done.Load() == 0 {
		defer o.done.Store(1)
		f()
	}
}

O código é extremamente simples no geral, com o seguinte fluxo:

  1. Carrega diretamente o valor atômico. Se já foi executado, retorna diretamente
  2. Se não foi executado, tenta obter o lock. Aqui pode haver múltiplas goroutines competindo pelo mutex
  3. Quando o vencedor obtém o lock com sucesso, ainda precisa verificar novamente o valor de done, pois enquanto você mantém o lock, outra goroutine pode já ter completado o ciclo de travar-executar-destravar. Neste momento você acabou de ser despertado
  4. Executa a função alvo
  5. Atualiza o valor de done
  6. Libera o lock

Resumo

sync.Once é uma ferramenta de sincronização muito concisa e eficiente, garantindo que uma operação específica seja executada apenas uma vez em ambiente concorrente, evitando assim trabalho repetido ou desperdício de recursos.

Golang por www.golangdev.cn edit