go sync包之singleflight原理

singlefight是什么 singlefight 直译为"单飞"(雅名到底是啥我也不知道), 顾名思义就是只有一个跑了, 是用来对同一资源控制并发 多个goroutine访问同一个资源时,只有一个goroutine真正的进行访问,其他goroutine等待这一个goroutine返回后共享返回结果 为什么出现singlefight 这个包 上面是什么中已经交代,是为了控制访问同一个资源的并发数,举个例子:假设有个接口访问数据库中id为1的一条数据,如果我们没有控制并发,那么来一百个并发访问这个数据,那么这一百个请求全部取请求数据库(即使有缓存也是全部请求缓存) 如果我们使用了singlefight那么,100个并发讲只有一个请求去数据库,其他99个全部共享那1个返回的结果 怎么用 var g = singleflight.Group{} //初始化了一个singleflight func SharedRes(id int) (int, error) { key := fmt.Sprintf("id:%d", id) //同一个group上,相同key的,只会执行一次,也就是说用key标识一个共享资源 ret, err, _ := g.Do(key, func() (interface{}, error) { //调用共享资源 time.Sleep(time.Second) //这里睡1s是模拟资源执行的延迟 fmt.Println("xxxx") return 1, nil }) return ret.(int), err } func SingleFlight() { wg := sync.WaitGroup{} //为了等100个goroutine执行完,开启了一个WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { //模拟并发 ret, err := SharedRes(1) fmt.Println("ret", ret, err) wg.Done() }() } wg.Wait() //等待100个goroutine执行完 } 看一下打印结果 xxxx <----这里只输出了一次xxxx ret 1 <nil> <----这里是返回结果 ret 1 <nil> ret 1 <nil> ret 1 <nil> .... 我们从现象可以看出,真正的业务逻辑执行只输出了一次,其他的goroutine也都返回了结果 源代码分析 Group结构 type Group struct { mu sync.Mutex // 互斥锁 m map[string]*call // 用来保存共享的调用结构,等会分析这个的作用 } call结构 type call struct { wg sync.WaitGroup //用来让共享groutine等待用的 //我们传入Do的函数签名是 func() (interface{}, error) val interface{} //函数返回结果 err error //函数返回错误 forgotten bool //调用Forget会forgotten=true,并从group.m 中删除这个key对应的call dups int //有几个一起共享结果的 chans []chan<- Result //通过DoChan配合使用,通过返回chan的方式返回结果 } Do函数分析,这个是这个包中最精华部分一之 ...

<span title='2022-06-19 22:02:23 +0800 +0800'>六月 19, 2022</span>