本篇文章会介绍go 开发小技巧。
type Semaphore chan struct{}
func NewSemaphore(maxCount int) Semaphore {
return make(chan struct{}, maxCount)
}
func (s Semaphore) Acquire() {
s <- struct{}{}
}
func (s Semaphore) tryAcquire() bool{
select {
case s <- struct{}{}:
default:
return false
}
return true
}
func (s Semaphore) Release() {
<-s
}
有点类似react的useMemo hook,会缓存函数结果
type SingleFlight struct {
m map[string]*call
}
type call struct {
sync.Once
res any
}
func newSingleFlight() *SingleFlight {
return &SingleFlight{
m: make(map[string]*call),
}
}
func (sf *SingleFlight) Do(key string, fn func() (any, error)) (any, error) {
if sf.m[key] != nil {
return sf.m[key].res, nil
}
ca := &call{}
var err error
ca.Once.Do(func() {
if res, e := fn(); e == nil {
ca.res = res
err = e
sf.m[key] = ca
}
})
return ca.res, err
}
func main() {
var sf = newSingleFlight()
var wg sync.WaitGroup
var t = time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
res, _ := sf.Do("longFunc", func() (any, error) {
time.Sleep(5 * time.Second)
return 5, nil
})
fmt.Println(res)
wg.Done()
}()
}
wg.Wait()
fmt.Println(time.Since(t))
}
once 可以用来处理只需要之心一次的结果
var (
once sync.Once
instance *Config
)
func GetConfig() *Config {
once.Do(func() {
instance = loadConfig()
})
return instance
}
err group 可以在调用线程获取并发执行goroute 的错误
func main() {
urls := []string {
"https://blog.devtrovert.com",
"https://example.com",
}
var g errgroup.Group
for _, url := range urls {
url := url // safe before Go 1.22
g.Go(func() error {
return fetch(url)
})
}
if err := g.Wait() ; err != nil {
log.Fatal(err)
}
}
Pool是对象池,可以复用对象
type Pool[T any] struct {
internal sync.Pool
}
func NewPool[T any](newF func() T) *Pool[T] {
return &Pool[T]{
internal: sync.Pool{
New: func() interface{} {
return newF()
},
},
}
}
func (p *Pool[T]) Get() T {
return p.internal.Get().(T)
}
func (p *Pool[T]) Put(v T) {
p.internal.Put(v)
}
1. 自定义error的粒度是类型,例如参数类型错误,可重试错误。
2.wrap或join。
func readConfig(path string) error {
return fmt.Errorf("read config: %w", ErrNotFound)
}
func main() {
err := readConfig("config.json")
if errors.Is(err, ErrNotFound) {
fmt.Println("config file not found")
}
}
func main() {
var errs = make([]error, 30)
var g sync.WaitGroup
for i := 0; i < 10; i++ {
g.Add(1)
j := i
go func(i int) {
errs = append(errs, errors.New(fmt.Sprintf("hello, %d", i)))
defer g.Done()
}(j)
}
g.Wait()
fmt.Println(errors.Join(errs...))
}
join 用于并发场景,将多个错误连接