一、简介
本篇文章会介绍go 开发小技巧。
二、go 开发技巧
2.1 Semaphore
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
}
2.2 singleflight
有点类似react的useMemo hook,会缓存函数结果
Go
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
}
demo
Go
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))
}
2.3 once
once 可以用来处理只需要之心一次的结果
Go
var (
once sync.Once
instance *Config
)
func GetConfig() *Config {
once.Do(func() {
instance = loadConfig()
})
return instance
}
2.4 error group
err group 可以在调用线程获取并发执行goroute 的错误
Go
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)
}
}
2.5 Pool
Pool是对象池,可以复用对象
Go
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
- 自定义error的粒度是类型,例如参数类型错误,可重试错误。
2.wrap或join。
Go
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")
}
}
Go
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 将多个错误连接
2.6 defer
测量函数执行时间
Go
func main() {
defer TrackTime(time.Now()) // <--- THIS
time.Sleep(500 * time.Millisecond)
}
func TrackTime(pre time.Time) time.Duration {
elapsed := time.Since(pre)
fmt.Println("elapsed:", elapsed)
return elapsed
}
// elapsed: 501.11125ms
2.7 实现接口判断
interface
Go
type Buffer interface {
Write(p []byte) (n int, err error)
}
type StringBuffer struct{}
判断StringBuffer 是否实现Buffer
Go
// syntax: var _ <interface> = (*type)(nil)
var _ Buffer = (*StringBuffer)(nil)