A thread-safe map implementation using sync.RWMutex to synchronize access. This package is optimized to handle concurrent reads and writes, providing efficient and reliable operations in multi-thread applications.
Go's standard map is not safe when accessed concurrently. This package wraps a map[K]V with sync.RWMutex to provide thread safety. With RWMutex, multiple readers can access the map at once, while writes are synchronized to prevent race conditions.
Main highlights:
- Thread-safety: Prevents data races in concurrent environments.
- Efficient Reads: Read operations (
Get,Range) allow simultaneous access without blocking. - Synchronous Write: Write operations (
Set,Delete,Getset) are synchronous.
This package is suitable when you need frequent reads and infrequent writes with a shared map.
go get github.com/yyle88/mutexmap package main
import (
"fmt"
"github.com/yyle88/mutexmap"
)
func main() {
mp := mutexmap.NewMap[string, int](10)
mp.Set("key1", 100)
mp.Set("key2", 200)
if value, found := mp.Get("key1"); found {
fmt.Println("Key1 Value:", value)
}
mp.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
}β¬οΈ Source: Source
package main
import (
"fmt"
"github.com/yyle88/mutexmap"
)
func main() {
mp := mutexmap.NewMap[string, string](10)
value, status := mp.Getset("exampleKey", func() string {
return "This is a computed value"
})
fmt.Println("Status:", status, "Value:", value)
value, status = mp.Getset("exampleKey", func() string {
return "New value"
})
fmt.Println("Status:", status, "Value:", value)
}β¬οΈ Source: Source
- Concurrent Access: Allows multiple goroutines to read and write to the map with thread protection.
- Optimized Reads: Supports simultaneous reads to improve performance.
- Custom Initialization: Use
Getsetto set a new value when the given ID does not exist.
| Method | Description |
|---|---|
NewMap[K comparable, V any](cap int) |
Creates a new Map with starting capacity hint. |
Get(k K) (V, bool) |
Retrieves the value associated with the ID. Returns false when the ID does not exist. |
Set(k K, v V) |
Associates a value with the given ID. Overwrites if the ID exists. |
Delete(k K) |
Removes the association from the map. |
Len() int |
Returns the count of associations in the map. |
Range(func(k K, v V) bool) |
Iterates through each association. Stops when the function returns false. |
Getset(k K, func() V) (V, enum) |
Gets a value and sets it when it doesn't exist, preventing duplicate computation. |
This section demonstrates various usage patterns and techniques with MutexMap.
Safe concurrent reads and writes:
mp := mutexmap.NewMap[string, int](10)
// Multiple goroutines can access the map with thread protection
go func() {
mp.Set("counter", 100)
}()
go func() {
if value, ok := mp.Get("counter"); ok {
fmt.Println("Value:", value)
}
}()Concurrent initialization with Getset:
mp := mutexmap.NewMap[string, *Database](10)
// Multiple goroutines requesting the same resource
// Just one creates it, the rest wait and receive the same instance
db, status := mp.Getset("primary", func() *Database {
return ConnectDatabase("primary")
})On-demand configuration loading:
configMap := mutexmap.NewMap[string, Config](10)
config, status := configMap.Getset("app", func() Config {
return LoadConfigFromFile("config.json")
})
// First call: status == mutexmap.CacheSet
// Subsequent calls: status == mutexmap.CacheGetDatabase connection pooling:
connMap := mutexmap.NewMap[string, *sql.DB](5)
conn, _ := connMap.Getset("mysql", func() *sql.DB {
db, _ := sql.Open("mysql", "user:pass@/dbname")
return db
})Check before update:
mp := mutexmap.NewMap[string, int](10)
mp.Set("score", 100)
if value, found := mp.Get("score"); found {
mp.Set("score", value+10) // Increment existing value
}Delete if exists:
if _, found := mp.Get("tempData"); found {
mp.Delete("tempData")
}Process each item:
mp.Range(func(key string, value int) bool {
fmt.Printf("%s: %d\n", key, value)
return true // Continue iteration
})Find specific entry:
mp.Range(func(key string, value int) bool {
if value > 100 {
fmt.Println("Found:", key)
return false // Stop iteration
}
return true
})Count matching entries:
count := 0
mp.Range(func(key string, value int) bool {
if value > threshold {
count++
}
return true
})String to struct mapping:
type Account struct {
ID int
Name string
}
accountMap := mutexmap.NewMap[string, Account](10)
accountMap.Set("user1", Account{ID: 1, Name: "Alice"})Int to function mapping:
handlerMap := mutexmap.NewMap[int, func() error](10)
handlerMap.Set(404, func() error {
return fmt.Errorf("not found")
})API response caching:
cache := mutexmap.NewMap[string, APIResponse](100)
response, status := cache.Getset(requestURL, func() APIResponse {
return FetchFromAPI(requestURL)
})Computed value caching:
resultCache := mutexmap.NewMap[string, int](50)
result, _ := resultCache.Getset("expensive-computation", func() int {
return PerformExpensiveCalculation()
})Session management:
sessions := mutexmap.NewMap[string, Session](100)
session, status := sessions.Getset(sessionID, func() Session {
return CreateNewSession(sessionID)
})Bulk insert:
data := map[string]int{"a": 1, "b": 2, "c": 3}
mp := mutexmap.NewMap[string, int](len(data))
for k, v := range data {
mp.Set(k, v)
}Bulk check:
keys := []string{"key1", "key2", "key3"}
found := make(map[string]bool)
for _, key := range keys {
if _, ok := mp.Get(key); ok {
found[key] = true
}
}- Thread Protection: Required when sharing maps across multiple threads.
- Efficient Reads: Read lock (
RLock) allows simultaneous read access without contention. - Write Protection: Write lock (
Lock) maintains data coherence during changes. - Smart Initialization: The
Getsetmethod avoids redundant computation.
MIT License. See LICENSE.
Contributions are welcome! Report bugs, suggest features, and contribute code:
- π Found a mistake? Open an issue on GitHub with reproduction steps
- π‘ Have a feature idea? Create an issue to discuss the suggestion
- π Documentation confusing? Report it so we can improve
- π Need new features? Share the use cases to help us understand requirements
- β‘ Performance issue? Help us optimize through reporting slow operations
- π§ Configuration problem? Ask questions about complex setups
- π’ Follow project progress? Watch the repo to get new releases and features
- π Success stories? Share how this package improved the workflow
- π¬ Feedback? We welcome suggestions and comments
New code contributions, follow this process:
- Fork: Fork the repo on GitHub (using the webpage UI).
- Clone: Clone the forked project (
git clone https://github.com/yourname/repo-name.git). - Navigate: Navigate to the cloned project (
cd repo-name) - Branch: Create a feature branch (
git checkout -b feature/xxx). - Code: Implement the changes with comprehensive tests
- Testing: (Golang project) Ensure tests pass (
go test ./...) and follow Go code style conventions - Documentation: Update documentation to support client-facing changes and use significant commit messages
- Stage: Stage changes (
git add .) - Commit: Commit changes (
git commit -m "Add feature xxx") ensuring backward compatible code - Push: Push to the branch (
git push origin feature/xxx). - PR: Open a merge request on GitHub (on the GitHub webpage) with detailed description.
Please ensure tests pass and include relevant documentation updates.
Welcome to contribute to this project via submitting merge requests and reporting issues.
Project Support:
- β Give GitHub stars if this project helps you
- π€ Share with teammates and (golang) programming friends
- π Write tech blogs about development tools and workflows - we provide content writing support
- π Join the ecosystem - committed to supporting open source and the (golang) development scene
Have Fun Coding with this package! πππ