Sling Academy
Home/Golang/Concurrency in Go with Maps: Using `sync.Map`

Concurrency in Go with Maps: Using `sync.Map`

Last updated: November 27, 2024

In Go, concurrency is a powerful feature that allows functions to run independently and interact through shared data. One challenging aspect of concurrency is safely handling shared data structures, particularly maps. In this article, we will explore how to use the `sync.Map` type to store and retrieve data concurrently in Go.

Understanding Maps in Go

Maps in Go are inherently not safe for concurrent use. This is because they are subject to race conditions, which occur when multiple goroutines read and write to a map at the same time. Let's review a simple example:

package main

import (
    "fmt"
)

func main() {
    m := make(map[string]int)
    m["a"] = 1
    fmt.Println(m["a"])
}

This basic use of a map is fine when accessed by a single goroutine. However, when multiple goroutines access a map simultaneously, it can lead to undefined behavior:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    m := make(map[string]int)
    
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", i)
            m[key] = i
        }(i)
    }
    wg.Wait()
    fmt.Println(m)
}

This example, when run, may cause a panic with the error: fatal error: concurrent map writes.

Introducing sync.Map

The `sync.Map` type is part of the standard library ("sync" package). It is designed for concurrent use and provides safe access to shared key-value pairs without the need to lock and unlock mutexes explicitly. Let's examine how `sync.Map` is used:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    var m sync.Map

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", i)
            m.Store(key, i)
        }(i)
    }
    wg.Wait()

    // Retrieve values from sync.Map
    m.Range(func(key, value interface{}) bool {
        fmt.Printf("%s = %d\n", key, value)
        return true
    })
}

In this enhanced version:

  • We replace the ordinary map with a `sync.Map`.
  • Use m.Store(key, value) for setting values.
  • Iterate using m.Range, which safely iterates through keys and values within the map.

Benefits and Limitations of sync.Map

`sync.Map` is particularly suitable for cases where frequent concurrent reads and writes occur, but it is important to consider some trade-offs:

  • Supports concurrent access by design, without additional locking mechanisms.
  • Optimal for maps with infrequent updates or when maintaining long-lived maps.
  • Slight overhead compared to a raw map due to managing keys and values as interface{} types.

Conclusion

Using sync.Map in Go offers a safe and efficient pathway for concurrent value storage without the intricacies of manual synchronization, especially important in high-performance applications handling concurrent tasks. When writing a program that requires concurrent map operations, consider leveraging `sync.Map` for its ease of use and concurrency-safe properties.

Next Article: Semaphores in Go: Controlling Resource Access

Previous Article: Implementing a Producer-Consumer Model in Go

Series: Concurrency and Synchronization in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant