Using GO Sync.Once

sync.Once is used to ensure that a function executes only once, regardless of how many goroutines attempt to execute it. It has one method Do(f func()) that takes the function to be executed.

Basic usage example:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var once sync.Once
    done := make(chan bool)

    // Simulate multiple goroutines calling
    for i := 0; i < 10; i++ {
        go func(x int) {
            // The function passed to once.Do() will only execute once
            once.Do(func() {
                fmt.Printf("Execute only once: %d\n", x)
            })
            // This line will be executed by every goroutine
            fmt.Printf("goroutine %d completed\n", x)
            done <- true
        }(i)
    }

    // Wait for all goroutines to complete
    for i := 0; i < 10; i++ {
        <-done
    }
}

Common use cases:

  1. Singleton pattern:

    type Singleton struct{}
    
    var (
        instance *Singleton
        once     sync.Once
    )
    
    func GetInstance() *Singleton {
        once.Do(func() {
            instance = &Singleton{}
        })
        return instance
    }
  2. One-time initialization:

    type Config struct {
        settings map[string]string
        once     sync.Once
    }
    
    func (c *Config) Load() {
        c.once.Do(func() {
            // Configuration loading code
            c.settings = make(map[string]string)
            c.settings["key"] = "value"
        })
    }
  3. Delayed resource closing:

    type Connection struct {
        once sync.Once
        quit chan struct{}
    }
    
    func (c *Connection) Close() {
        c.once.Do(func() {
            close(c.quit)
        })
    }
  4. Error handling:

    type ErrorHandler struct {
        once  sync.Once
        err   error
    }
    
    func (e *ErrorHandler) HandleError(err error) {
        e.once.Do(func() {
            e.err = err
            // Only handle the first error
            fmt.Println("Handling error:", err)
        })
    }

Characteristics of sync.Once:

  1. Thread-safe: Can be safely used across multiple goroutines
  2. Execute only once: No matter how many times it’s called, the passed function will only execute once
  3. All goroutines will wait for the first execution to complete
  4. No retry on failure: If the function execution fails, subsequent calls will not re-execute
  5. Cannot be reset: Once executed, it cannot be restarted

It’s important to note that sync.Once’s Do method only guarantees the function executes once, but doesn’t guarantee which goroutine executes it. If multiple goroutines call simultaneously, only one will execute the function while the others wait for completion.

0%