Welcome to our deep dive into the fascinating world of synchronization in Go! Today, we'll explore the sync.Once type, a powerful tool that ensures a piece of code is executed at most once in a Go program. Let's get started!
sync.Once? 📝The sync.Once type is a Go synchronization primitive that guarantees the execution of a block of code exactly once during the lifetime of the program. It's particularly useful when you want to perform an expensive initialization task or set up a resource that should only be initialized once.
sync.Once object 💡To create a sync.Once object, simply call the sync.Once function.
package main
import (
"fmt"
"sync"
)
var initOnce sync.OnceIn the code above, we've declared a sync.Once variable named initOnce.
sync.Once to initialize a resource 💡Now let's see how to use sync.Once to initialize a resource safely. In this example, we'll create a database connection that should only be established once.
package main
import (
"database/sql"
"fmt"
"log"
"sync"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
var initDB sync.Once
func initDatabase() *sql.DB {
var err error
db, err = sql.Open("mysql", "user:pass@tcp(localhost:3306)/database_name")
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
initDB.Do(initDatabase)
fmt.Println("Database connection established.")
// Use the database connection here
}In the code above, we've defined a function initDatabase() that sets up a MySQL database connection. We've also created a sync.Once variable initDB and used the Do() method to execute the initDatabase() function exactly once.
Do() method 💡The Do() method takes a closure (a function with access to the surrounding variables) as an argument and ensures that the closure is only executed once during the lifetime of the sync.Once object.
You can also use the sync.Once object multiple times within a single program. In such cases, the Do() method will only execute the closure if it hasn't been executed before.
package main
import (
"fmt"
"sync"
)
var initOnce sync.Once
func init() {
initOnce.Do(func() {
fmt.Println("Initialization complete.")
})
}
func main() {
initOnce.Do(func() {
fmt.Println("Initialization attempted again.")
})
fmt.Println("Main function execution.")
}In the code above, we've defined a function init() that initializes the program. We've also declared a global sync.Once variable initOnce. Inside the main() function, we attempt to execute the closure of initOnce again, but since it has already been executed, nothing happens.
What guarantees the execution of a block of code exactly once during the lifetime of a Go program using `sync.Once`?
That's all for today! We've covered the basics of the sync.Once type in Go and seen how it can be used to ensure controlled execution of code. In the next lesson, we'll dive deeper into Go synchronization primitives and explore the sync.Mutex type. Stay tuned! 🚀