Sync.Once()


✅ Mục tiêu của sync.Once

sync.Once được dùng để đảm bảo một đoạn mã chỉ được thực thi một lần duy nhấtdù có bao nhiêu goroutine gọi nó đồng thời.

📌 Use case phổ biến

  • Khởi tạo singleton

  • Đăng ký handlers hoặc middleware một lần

  • Thiết lập kết nối database / caching lần đầu tiên


🔧 Cách sử dụng sync.Once

var once sync.Once

func initialize() {
    fmt.Println("Initializing...")
}

func main() {
    for i := 0; i < 5; i++ {
        go func() {
            once.Do(initialize)
        }()
    }

    time.Sleep(time.Second) // đợi goroutine chạy xong
}

✅ Kết quả in ra: duy nhất 1 lần

Initializing...

⚠️ Lưu ý quan trọng

1. once.Do(f) sẽ gọi f() một lần duy nhất trong suốt runtime.

Dù bạn gọi once.Do() bao nhiêu lần hay từ bao nhiêu goroutine, thì f() chỉ được thực thi duy nhất một lần.


2. Nếu f() panic — sync.Once coi như đã thực thi

var once sync.Once

func panicFunc() {
    fmt.Println("Panic here")
    panic("oops")
}

func main() {
    once.Do(panicFunc) // panic xảy ra ở đây
    once.Do(func() {
        fmt.Println("This will NOT run")
    })
}

⚠️ Một khi f() panic, mọi lần gọi tiếp theo cũng sẽ bị bỏ qua.


✅ Ứng dụng thực tế — Singleton pattern

type Config struct {
    DBHost string
}

var configInstance *Config
var once sync.Once

func GetConfig() *Config {
    once.Do(func() {
        fmt.Println("Loading config...")
        configInstance = &Config{DBHost: "localhost"}
    })
    return configInstance
}

🧠 Tổng kết

Thuộc tính
Giá trị

Thread-safe

Chỉ chạy một lần

Không reset lại được

Dùng trong init logic


Nếu bạn muốn một cơ chế “chỉ chạy một lần” nhưng có khả năng reset hoặc nhiều lần tùy điều kiện, thì bạn sẽ cần dùng sync.Mutex hoặc sync.OnceValue (Go 1.22+).


Last updated