MST

星途 面试题库

面试题:Go语言Map在并发场景下的问题与解决方案

在Go语言的并发编程中,使用Map可能会遇到哪些问题?请举例说明。针对这些问题,有哪些常见的解决方案?请详细阐述并给出代码示例。
36.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用Map可能遇到的问题

  1. 并发读写问题:Go语言中的Map不是线程安全的,当多个goroutine同时对一个Map进行读写操作时,可能会导致数据竞争,进而出现未定义行为,比如程序崩溃或得到错误的结果。 例如:
package main

import (
    "fmt"
)

func main() {
    var m = make(map[string]int)
    for i := 0; i < 10; i++ {
        go func(n int) {
            key := fmt.Sprintf("key%d", n)
            m[key] = n
        }(i)
    }
    fmt.Println(m)
}

上述代码在多个goroutine并发写Map时,可能会引发数据竞争,运行时可能会崩溃并提示类似 fatal error: concurrent map writes 的错误。

常见解决方案

  1. 使用sync.Mutex:通过互斥锁来保护对Map的读写操作,确保同一时间只有一个goroutine可以访问Map。 示例代码:
package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    var m = make(map[string]int)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", n)
            mu.Lock()
            m[key] = n
            mu.Unlock()
        }(i)
    }
    wg.Wait()
    mu.Lock()
    fmt.Println(m)
    mu.Unlock()
}
  1. 使用sync.RWMutex:如果读操作远多于写操作,可以使用读写互斥锁。它允许多个goroutine同时进行读操作,但写操作时会独占Map。 示例代码:
package main

import (
    "fmt"
    "sync"
)

func main() {
    var rwmu sync.RWMutex
    var m = make(map[string]int)
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", n)
            rwmu.Lock()
            m[key] = n
            rwmu.Unlock()
        }(i)
    }
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            rwmu.RLock()
            fmt.Println(m)
            rwmu.RUnlock()
        }()
    }
    wg.Wait()
}
  1. 使用sync.Map:Go 1.9 引入了 sync.Map,这是一个线程安全的Map实现。它适用于高并发场景,在不需要额外锁的情况下支持并发读写。 示例代码:
package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", n)
            m.Store(key, n)
        }(i)
    }
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            m.Range(func(key, value interface{}) bool {
                fmt.Printf("key: %v, value: %v\n", key, value)
                return true
            })
        }()
    }
    wg.Wait()
}