探秘Go并发利器:Singleflight深入剖析
2023-11-03 20:06:12
Singleflight简介
Singleflight是一个轻量级的并发控制库,用于处理共享数据访问的场景。它可以确保在多个并发请求同时访问共享数据时,只有一个请求会真正执行数据获取操作,其他请求都会等待该请求的返回结果。
Singleflight通过使用一个全局的map来存储共享数据。当一个并发请求首次访问共享数据时,Singleflight会检查map中是否已经存在该数据的副本。如果存在,则直接返回该副本;如果不存在,则创建一个新的goroutine来获取数据,并将该goroutine的结果存储在map中。后续的并发请求在访问共享数据时,都会直接从map中获取数据,而无需重新获取。
Singleflight的使用方法
Singleflight的使用非常简单,只需要导入singleflight包,并创建一个Singleflight实例即可。
import "github.com/golang/singleflight"
var sf = singleflight.Group{}
Singleflight实例创建后,就可以使用Do方法来获取共享数据。Do方法接收两个参数:key和f。key是共享数据的唯一标识,f是获取共享数据的函数。
data, err := sf.Do("key", func() (interface{}, error) {
// 从数据库中获取数据
return nil, nil
})
Do方法会先检查map中是否已经存在key对应的共享数据。如果存在,则直接返回该数据;如果不存在,则创建一个新的goroutine来获取数据,并将该goroutine的结果存储在map中。后续的并发请求在访问共享数据时,都会直接从map中获取数据,而无需重新获取。
Singleflight的源码解读
Singleflight的源码非常简洁,只有不到100行代码。下面我们将从源码层面来了解Singleflight是如何工作的。
Singleflight的核心数据结构是一个全局的map,用于存储共享数据。map的key是共享数据的唯一标识,map的value是一个*call结构体。
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
call结构体包含了一个sync.WaitGroup对象、一个interface{}类型的val字段和一个error类型的err字段。sync.WaitGroup对象用于等待goroutine完成数据获取操作,val字段存储获取到的数据,err字段存储获取数据时发生的错误。
Singleflight的Do方法首先会检查map中是否已经存在key对应的call结构体。如果存在,则直接返回该call结构体的val字段和err字段。如果不存在,则创建一个新的goroutine来获取数据,并将该goroutine的结果存储在map中。
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
// 检查map中是否已经存在key对应的call结构体
c, ok := g.mu.Load(key)
if !ok {
// 创建一个新的call结构体
c = &call{
wg: sync.WaitGroup{},
}
// 将call结构体存储在map中
g.mu.Store(key, c)
// 创建一个新的goroutine来获取数据
go func() {
defer c.wg.Done()
// 获取数据
c.val, c.err = fn()
}()
}
// 等待goroutine完成数据获取操作
c.wg.Wait()
// 返回获取到的数据和错误
return c.val, c.err
}
Singleflight使用过程中的“坑”
在使用Singleflight的过程中,可能会遇到一些“坑”。下面我们将介绍一些常见的“坑”以及如何避免这些“坑”。
1. 数据获取函数中使用外部变量
在Singleflight的数据获取函数中,如果使用了外部变量,则需要小心处理该变量的并发访问问题。因为在并发场景下,多个goroutine可能会同时执行数据获取函数,从而导致外部变量出现数据竞争问题。
为了避免这种情况,可以将外部变量复制一份到数据获取函数中,或者使用sync.Mutex等锁机制来保护外部变量的并发访问。
2. 数据获取函数中执行耗时操作
在Singleflight的数据获取函数中,如果执行了耗时操作,则可能会导致后续的并发请求长时间等待。为了避免这种情况,可以将耗时操作放到 goroutine中执行,并在数据获取函数中等待goroutine的返回结果。
3. 数据获取函数中发生错误
在Singleflight的数据获取函数中,如果发生了错误,则后续的并发请求都会收到相同的错误。为了避免这种情况,可以对数据获取函数中的错误进行处理,并只将处理后的结果返回给后续的并发请求。
结语
Singleflight是一个非常有用的并发控制库,它可以帮助我们轻松处理共享数据访问的场景。通过本文的介绍,相信你已经对Singleflight有了一定的了解。希望你能够在实际项目中使用Singleflight来优化你的程序性能。