MST

星途 面试题库

面试题:Go语言切片延迟初始化与懒加载在高并发场景下的挑战与应对

在高并发场景下,Go语言切片的延迟初始化与懒加载模式会面临哪些挑战?例如数据竞争、资源争用等问题。请提出至少两种有效的解决方案,并结合具体代码示例说明如何实现线程安全的切片延迟初始化与懒加载。
22.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

挑战

  1. 数据竞争:在高并发环境下,多个 goroutine 可能同时尝试初始化切片,导致数据竞争问题,即不同 goroutine 对切片的初始化操作相互干扰,造成结果不可预测。
  2. 资源争用:如果初始化操作涉及到资源获取(如从数据库读取数据填充切片),多个 goroutine 同时进行初始化可能导致资源争用,降低系统性能。

解决方案

1. 使用sync.Once

sync.Once 类型提供了一种简单的机制来确保函数只执行一次,常用于延迟初始化。

package main

import (
    "fmt"
    "sync"
)

var (
    data []int
    once sync.Once
)

func getData() []int {
    once.Do(func() {
        // 模拟初始化操作,例如从数据库读取数据
        data = make([]int, 10)
        for i := range data {
            data[i] = i
        }
    })
    return data
}

在上述代码中,once.Do 确保 make([]int, 10) 以及后续的数据填充操作只执行一次,无论多少个 goroutine 调用 getData 函数,从而避免了数据竞争和重复初始化。

2. 使用互斥锁 sync.Mutex

通过互斥锁可以在初始化切片时进行加锁,防止多个 goroutine 同时进行初始化。

package main

import (
    "fmt"
    "sync"
)

var (
    data []int
    mu   sync.Mutex
    initialized bool
)

func getData() []int {
    if!initialized {
        mu.Lock()
        defer mu.Unlock()
        if!initialized {
            // 模拟初始化操作
            data = make([]int, 10)
            for i := range data {
                data[i] = i
            }
            initialized = true
        }
    }
    return data
}

这里先进行快速检查 initialized,如果未初始化则加锁,再次检查 initialized 以防止多个 goroutine 同时进入初始化部分,最后解锁。这种方式虽然比 sync.Once 复杂一些,但也能有效地实现线程安全的切片延迟初始化。