go 开发小技巧
一、简介
本篇文章会介绍go 开发小技巧。
二、go 开发技巧
2.1 Semaphore
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
}
2.2 singleflight
有点类似react的useMemo hook,会缓存函数结果
type SingleFlight struct {m map[string]*call
}type call struct {sync.Onceres 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 errorca.Once.Do(func() {if res, e := fn(); e == nil {ca.res = reserr = esf.m[key] = ca}})return ca.res, err
}
demo
func main() {var sf = newSingleFlight()var wg sync.WaitGroupvar 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))
}
2.3 once
once 可以用来处理只需要之心一次的结果
var (once sync.Onceinstance *Config
)func GetConfig() *Config {once.Do(func() {instance = loadConfig()})return instance
}
2.4 error group
err group 可以在调用线程获取并发执行goroute 的错误
func main() {urls := []string {"https://blog.devtrovert.com","https://example.com",}var g errgroup.Groupfor _, url := range urls {url := url // safe before Go 1.22g.Go(func() error {return fetch(url)})}if err := g.Wait() ; err != nil {log.Fatal(err)}
}
2.5 Pool
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)
}
2.6 error
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.WaitGroupfor i := 0; i < 10; i++ {g.Add(1)j := igo 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 用于并发场景,将多个错误连接