MST

星途 面试题库

面试题:Go语言函数的并发安全设计

假设你正在设计一个高并发的Go应用,其中有多个函数会共享一些资源。请详细描述你会采取哪些措施来确保这些函数在并发环境下的安全性,包括但不限于使用sync包中的工具,并给出完整的代码示例展示如何实现。
18.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 使用互斥锁(Mutex)
    • 互斥锁是一种常用的同步工具,用于保护共享资源。在Go语言中,sync.Mutex可以实现这一功能。
    • 当一个函数需要访问共享资源时,它首先获取互斥锁,在访问完成后释放互斥锁,这样其他函数在同一时间就无法访问该资源。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mu      sync.Mutex
)

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    counter++
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    wg.Wait()
    fmt.Println("Final counter value:", counter)
}
  1. 读写锁(RWMutex)
    • 如果共享资源的读取操作远远多于写入操作,可以使用读写锁。sync.RWMutex允许同一时间有多个读操作,但只允许一个写操作。
    • 读操作使用RLock方法获取锁,写操作使用Lock方法获取锁。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

var (
    data  int
    rwmu  sync.RWMutex
)

func read(wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.RLock()
    fmt.Println("Read data:", data)
    rwmu.RUnlock()
}

func write(wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.Lock()
    data++
    fmt.Println("Write data:", data)
    rwmu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go read(&wg)
    }
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go write(&wg)
    }
    wg.Wait()
}
  1. 使用通道(Channel)
    • 通道是Go语言中用于在不同的goroutine之间进行通信的机制。通过将共享资源的访问封装在通道操作中,可以避免显式地使用锁。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    value int
}

func newCounter() *Counter {
    return &Counter{}
}

func (c *Counter) increment(ch chan struct{}) {
    <-ch
    c.value++
    ch <- struct{}{}
}

func (c *Counter) get(ch chan struct{}) int {
    <-ch
    v := c.value
    ch <- struct{}{}
    return v
}

func main() {
    var wg sync.WaitGroup
    counter := newCounter()
    ch := make(chan struct{}, 1)
    ch <- struct{}{}
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.increment(ch)
        }()
    }
    wg.Wait()
    fmt.Println("Final counter value:", counter.get(ch))
    close(ch)
}
  1. 使用sync.Map
    • sync.Map是Go 1.9引入的一个线程安全的映射。它适用于高并发读写的场景,无需额外的锁机制。
    • 示例代码如下:
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(n int) {
            defer wg.Done()
            m.Store(n, n*n)
        }(i)
    }
    go func() {
        wg.Wait()
        m.Range(func(key, value interface{}) bool {
            fmt.Printf("Key: %d, Value: %d\n", key, value)
            return true
        })
    }()
    wg.Wait()
}