// SharedCalls接口提供了Do和DoEx兩種方法
type SharedCalls interface {Do(key string, fn func() (interface{}, error)) (interface{}, error)DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error)
}
// call代表對指定資源的一次請求
type call struct {wg sync.WaitGroup // 用于協調各個請求goroutine之間的資源共享val interface{} // 用于保存請求的返回值err error // 用于保存請求過程中發生的錯誤
}type sharedGroup struct {calls map[string]*calllock sync.Mutex
}
// 當多個請求同時使用Do方法請求資源時
func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {// 先申請加鎖g.lock.Lock()// 根據key,獲取對應的call結果,并用變量c保存if c, ok := g.calls[key]; ok {// 拿到call以后,釋放鎖,此處call可能還沒有實際數據,只是一個空的內存占位g.lock.Unlock()// 調用wg.Wait,判斷是否有其他goroutine正在申請資源,如果阻塞,說明有其他goroutine正在獲取資源c.wg.Wait()// 當wg.Wait不再阻塞,表示資源獲取已經結束,可以直接返回結果return c.val, c.err}// 沒有拿到結果,則調用makeCall方法去獲取資源,注意此處仍然是鎖住的,可以保證只有一個goroutine可以調用makecallc := g.makeCall(key, fn)// 返回調用結果return c.val, c.err
}
func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) {g.lock.Lock()if c, ok := g.calls[key]; ok {g.lock.Unlock()c.wg.Wait()return c.val, false, c.err}c := g.makeCall(key, fn)return c.val, true, c.err
}
// 進入makeCall的一定只有一個goroutine,因為要拿鎖鎖住的
func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call {// 創建call結構,用于保存本次請求的結果c := new(call)// wg加1,用于通知其他請求資源的goroutine等待本次資源獲取的結束c.wg.Add(1)// 將用于保存結果的call放入map中,以供其他goroutine獲取g.calls[key] = c// 釋放鎖,這樣其他請求的goroutine才能獲取call的內存占位g.lock.Unlock()defer func() {// delete key first, done later. can't reverse the order, because if reverse,// another Do call might wg.Wait() without get notified with wg.Done()g.lock.Lock()delete(g.calls, key)g.lock.Unlock()// 調用wg.Done,通知其他goroutine可以返回結果,這樣本批次所有請求完成結果的共享c.wg.Done()}()// 調用fn方法,將結果填入變量c中c.val, c.err = fn()return c
}
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态