You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
67 lines
1.3 KiB
67 lines
1.3 KiB
package syncx |
|
|
|
import "sync" |
|
|
|
type ( |
|
SingleFlight interface { |
|
Do(key string, fn func() (any, error)) (any, error) |
|
DoEx(key string, fn func() (any, error)) (any, bool, error) |
|
} |
|
call struct { |
|
wg sync.WaitGroup |
|
val any |
|
err error |
|
} |
|
flightGroup struct { |
|
calls map[string]*call |
|
lock sync.Mutex |
|
} |
|
) |
|
|
|
func NewSingleFlight() SingleFlight { |
|
return &flightGroup{ |
|
calls: make(map[string]*call), |
|
} |
|
} |
|
|
|
func (g *flightGroup) Do(key string, fn func() (any, error)) (any, error) { |
|
c, done := g.createCall(key) |
|
if done { |
|
return c.val, c.err |
|
} |
|
g.makeCall(c, key, fn) |
|
return c.val, c.err |
|
} |
|
|
|
func (g *flightGroup) DoEx(key string, fn func() (any, error)) (v any, fresh bool, err error) { |
|
c, done := g.createCall(key) |
|
if done { |
|
return c.val, false, c.err |
|
} |
|
g.makeCall(c, key, fn) |
|
return c.val, true, c.err |
|
} |
|
func (g *flightGroup) createCall(key string) (c *call, done bool) { |
|
g.lock.Lock() |
|
//有在执行的等待结果返回 |
|
if c, ok := g.calls[key]; ok { |
|
g.lock.Unlock() |
|
c.wg.Wait() |
|
return c, true |
|
} |
|
//创建 |
|
c = new(call) |
|
c.wg.Add(1) |
|
g.calls[key] = c |
|
g.lock.Unlock() |
|
return c, false |
|
} |
|
func (g *flightGroup) makeCall(c *call, key string, fn func() (any, error)) { |
|
defer func() { |
|
g.lock.Lock() |
|
delete(g.calls, key) |
|
g.lock.Unlock() |
|
c.wg.Done() |
|
}() |
|
c.val, c.err = fn() |
|
}
|
|
|