Skip to main content

Command Palette

Search for a command to run...

Golang sync map

Published
•3 min read•View as Markdown

What is sync.Map

sync map is concurrent safe map data type.

Use Cases

used to store keys and values in multiple concurrent goroutines.

Example

valueStore is a sync map used to store values in multiple goroutines. after all goroutines finished store data in valueStore, Call Range to get all keys and values to result.

package main

import (
    "fmt"
    "sync"
)

func geValuesByKey(key string) []int {
    switch key {
    case "k1":
        return []int{1, 2, 3}
    case "k2":
        return []int{100, 200, 300}
    case "k6":
        return []int{8, 9, 10}
    }

    return []int{}
}

func concurrentGetValues(keys []string) map[string][]int {
    var valueStore sync.Map
    var wg sync.WaitGroup

    for _, key := range keys {
        wg.Add(1)
        go func(k string) {
            values := geValuesByKey(k)
            if len(values) > 0 {
                valueStore.Store(k, values)
            }
            wg.Done()
        }(key)
    }
    wg.Wait()

    result := make(map[string][]int, len(keys))
    valueStore.Range(func(key, value interface{}) bool {
        result[key.(string)] = value.([]int)
        return true
    })
    return result
}

func main() {
    keys := []string{"k1", "k2", "k6", "k3"}
    result := concurrentGetValues(keys)

    fmt.Printf("concurrentGetValues: %+v\n", result) // concurrentGetValues: map[k1:[1 2 3] k2:[100 200 300] k6:[8 9 10]]
}

The performance of sync map vs locked map

package bench

import "sync"

type LockedStore struct {
    sync.RWMutex
    m map[string]interface{}
}

func (s *LockedStore) Get(key string) interface{} {
    s.RLock()
    defer s.RUnlock()
    return s.m[key]
}

func (s *LockedStore) Set(key string, value interface{}) {
    s.Lock()
    s.m[key] = value
    s.Unlock()
}

Bench mark

package bench

import (
    "strconv"
    "sync"
    "testing"
)

var (
    number      = 1000000
    lockedStore = initStore(number, false)
    syncStore   = initSyncMap(number, false)
)

func initStore(n int, noValue bool) *LockedStore {
    s := &LockedStore{m: make(map[string]interface{}, n)}
    if noValue {
        return s
    }

    var i int
    for ; i < n; i++ {
        s.Set(strconv.Itoa(i), i)
    }
    return s
}

func initSyncMap(n int, noValue bool) sync.Map {
    var m sync.Map
    if noValue {
        return m
    }

    for i := 0; i < n; i++ {
        m.Store(strconv.Itoa(i), i)
    }
    return m
}

func BenchmarkSyncMapGet(b *testing.B) {
    n := b.N
    var wg sync.WaitGroup
    wg.Add(n)
    for i := 0; i < n; i++ {
        k := strconv.Itoa(i)
        go func(key string) {
            syncStore.Load(key)
            wg.Done()
        }(k)
    }
    wg.Wait()
}

func BenchmarkSyncMapSet(b *testing.B) {
    n := b.N
    syncStore = initSyncMap(n, true)

    var wg sync.WaitGroup
    wg.Add(n)
    for i := 0; i < n; i++ {
        k := strconv.Itoa(i)
        go func(key string) {
            syncStore.Store(key, ":"+key+":")
            wg.Done()
        }(k)
    }
    wg.Wait()
}

func BenchmarkLockedStore_Get(b *testing.B) {
    n := b.N
    var wg sync.WaitGroup
    wg.Add(n)
    for i := 0; i < n; i++ {
        k := strconv.Itoa(i)
        go func(key string) {
            lockedStore.Get(key)
            wg.Done()
        }(k)
    }
    wg.Wait()
}

func BenchmarkLockedStore_Set(b *testing.B) {
    n := b.N
    lockedStore = initStore(n, true)
    var wg sync.WaitGroup
    wg.Add(n)
    for i := 0; i < n; i++ {
        k := strconv.Itoa(i)
        go func(key string) {
            lockedStore.Set(key, ":"+key+":")
            wg.Done()
        }(k)
    }
    wg.Wait()
}
/*Bench Result
go test -bench=.
goos: darwin
goarch: amd64
pkg: go-common/bench
BenchmarkSyncMapGet-8            1000000              1051 ns/op
BenchmarkSyncMapSet-8             805191              2834 ns/op
BenchmarkLockedStore_Get-8       2943969               390 ns/op
BenchmarkLockedStore_Set-8       1281457               989 ns/op
*/