Goroutine 本地存储指每个 Goroutine 独立维护的存储空间。对于同一个程序,不同的 Goroutine 可以访问自己本地存储空间中的数据,但无法访问其他 Goroutine 的存储空间。
在 Golang 中,可以使用 sync.Map 作为 Goroutine 本地存储:
var localStore = sync.Map{}
func doSomething() {
// 在当前 Goroutine 中访问本地存储
localStore.Store("key", "value")
value, _ := localStore.Load("key")
}
func doAnotherThing() {
// 在其他 Goroutine 中无法访问先前存储的数据
value, ok := localStore.Load("key")
// ok 为 false,无法获取值
}
这里 localStore 是一个全局的 sync.Map,但是由于其内部使用 Goroutine ID 作为键名访问数据,所以每个 Goroutine 只能看到自己存放的数据,实现了本地存储的效果。
我们也可以使用原子操作来构造一个简单的 Goroutine 本地存储:
type localStore struct {
store map[string]interface{}
mu sync.Mutex
}
func newLocalStore() *localStore {
return &localStore{
store: make(map[string]interface{}),
}
}
func (l *localStore) Load(key string) (interface{}, bool) {
l.mu.Lock() // 加锁
value, ok := l.store[key]
l.mu.Unlock() // 解锁
return value, ok
}
func (l *localStore) Store(key string, value interface{}) {
l.mu.Lock()
l.store[key] = value
l.mu.Unlock()
}
这里使用 sync.Mutex 来对localStore 加锁,在每个 Goroutine 中锁住后可以访问本地 map 数据,实现了本地存储的效果。
所以 Goroutine 本地存储的主要作用是为每个 Goroutine 提供独立的数据层,使得不同 Goroutine 无法相互访问对方的本地数据。这在一定程度上可以简化并发程序的设计,避免一些竞态条件下的 bug。
但是滥用 Goroutine 本地存储也会带来数据隔离过度,降低程序结构的清晰度。所以在使用时,我们需要权衡其带来的优点与可能的负面影响。