1 回答

TA貢獻1802條經驗 獲得超10個贊
這里的問題是時間和Forget方法行為的結合。正如文檔中所述:
Forget 告訴 singleflight 忘記一把鑰匙。未來為此鍵調用 Do 將調用該函數,而不是等待較早的調用完成。
那里的Future意味著所有對 的調用都group.Do發生在對 的調用之后group.Forget。在您的示例中,對的所有調用都group.Do發生在調用之前group.Forget,并且所有調用都獲得了第一個失敗調用的結果。可能的方法是在 `group.Do 調用之外進行觸發重試。是這樣的:
package main
import (
"context"
"errors"
"log"
"math/rand"
"sync/atomic"
"time"
"golang.org/x/sync/singleflight"
)
type Result struct {
v int
k string
}
var group singleflight.Group
func main() {
for k := 0; k <= 2; k++ {
go doGroup(context.Background(), "sameKey")
}
<-time.Tick(5 * time.Second)
for k := 0; k <= 3; k++ {
go doGroup(context.Background(), "sameKey")
}
<-time.Tick(30 * time.Second)
}
func doGroup(ctx context.Context, key string) (*Result, error) {
log.Println("Inside normal call")
for {
results, err, shared := group.Do(key, func() (interface{}, error) {
return doExpensive(ctx, key)
})
if err != nil {
log.Printf("Normal call error: %s. Will retry \n", err)
continue
}
log.Printf("Normal call results: %v [shared=%v]\n", results, shared)
return results.(*Result), err
}
}
var returnedFirstErr atomic.Bool
func doExpensive(ctx context.Context, key string) (r *Result, e error) {
log.Printf("Inside Expensive function with key %s\n", key)
defer func() {
log.Printf("Result of Expensive function: [%v, %s] for %s\n", r, e, key)
}()
<-time.Tick(time.Second * 10)
dice := rand.Int31n(10)
if !returnedFirstErr.Load() {
returnedFirstErr.Store(true)
return nil, errors.New("operation failed")
}
return &Result{
v: int(dice),
k: key,
}, nil
}
旁問。您確定 的行為singleflight是您所需要的,也許您應該改用sync.Once?如果singleflight您阻止多個調用同時發生,即仍然會執行稍后完成的調用。如果sync.Once調用在進程的生命周期內恰好完成一次
- 1 回答
- 0 關注
- 114 瀏覽
添加回答
舉報