sync.Map
is a concurrent map implementation in Go, designed to be safe for concurrent use by multiple goroutines. It provides a way to safely store and retrieve key-value pairs in a map-like structure, without requiring explicit locking and unlocking using sync.Mutex
.
It’s particularly useful in scenarios where you have many goroutines reading and writing to the map simultaneously. The map is optimized for cases where reads are more frequent than writes, making it suitable for caches, lookups, or storing shared data in highly concurrent applications.
- Purpose: Provides thread-safe operations for maps.
- How it works: Uses fine-grained locks internally, which improves performance compared to using
sync.Mutex
in highly concurrent environments.
-
Create a sync.Map
- You can create a
sync.Map
by initializing it with an empty struct.
- You can create a
-
Store and Retrieve Values
- Use
Store(key, value)
to store a key-value pair. - Use
Load(key)
to retrieve a value by key. - Use
Delete(key)
to remove a key-value pair. - Use
LoadOrStore(key, value)
to retrieve the value for a key, or store the value if it doesn’t exist.
- Use
-
Iterate Over sync.Map
- You can iterate over the map using
Range
, which allows you to apply a function to each key-value pair.
- You can iterate over the map using
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
m.Store("user_id", 98765)
m.Store("user_name", "dark_vard")
m.Store("account_balance", 9999.99)
m.Store("subscription_active", true)
m.Store("last_login", "2024-12-01")
fmt.Println("Iterating over map and printing key-value pairs:")
m.Range(func(key, value any) bool {
if key == "account_balance" {
fmt.Println("-> Found account_balance, stopping iteration.")
return false
}
fmt.Printf("Key: %v, Value: %v\n", key, value)
return true
})
fmt.Println("Deleting 'last_login' key...")
m.Delete("last_login")
if value, ok := m.Load("last_login"); ok {
fmt.Println("Found last_login:", value)
} else {
fmt.Println("Last login was deleted.")
}
fmt.Println("Using LoadOrStore for 'last_login'...")
if value, loaded := m.LoadOrStore("last_login", "2024-12-01"); loaded {
fmt.Println("Loaded existing value for last_login:", value)
} else {
fmt.Println("Stored new value for last_login:", value)
}
}
- Concurrency-Safe Operations:
sync.Map
allows for concurrent access, eliminating the need for explicit locking. - Optimized for Read-Heavy Operations: It performs well in environments where read operations are much more frequent than writes.
- Lazy Initialization: It supports lazy loading of values using
LoadOrStore
.
-
Cache Implementation
- Scenario: A caching system needs to store and retrieve data frequently with high concurrency.
- Solution: Use
sync.Map
to store cache entries and ensure thread-safe access without locking overhead.
-
Global State Management
- Scenario: When managing global state shared between many goroutines (e.g., configuration settings, feature flags).
- Solution: Use
sync.Map
for efficient and thread-safe access to shared global data.
-
Counting or Tracking Metrics
- Scenario: Tracking counts, logs, or metrics that are frequently updated by different goroutines.
- Solution: Use
sync.Map
to store counts, and update them concurrently in a thread-safe manner.
- Avoid Excessive Deletions: Frequently deleting keys in
sync.Map
could affect performance, as it internally uses a technique to handle deleted items efficiently. - Key Type: The key type should be comparable, meaning it can be used in equality comparisons (e.g., strings, integers).
- Use for Read-Heavy Scenarios:
sync.Map
shines in read-heavy workloads, so it’s not ideal for scenarios with frequent writes and deletions.
To use sync.Map
effectively:
- Use for Read-Heavy Workloads:
sync.Map
is ideal for scenarios where reads are more frequent than writes. - Avoid Frequent Deletions: Frequent deletes could harm performance, so be mindful of using
Delete
often. - Ensure Proper Key Comparability: Only use types as keys that are comparable (e.g., strings, integers).