一、简介
本篇文章会介绍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.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
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 可以用来处理只需要之心一次的结果
var (
once sync.Once
instance *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.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是对象池,可以复用对象
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.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.7 defer
测量函数执行时间
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.8 实现接口判断
interface
type Buffer interface {
Write(p []byte) (n int, err error)
}
type StringBuffer struct{}
判断StringBuffer 是否实现Buffer
// syntax: var _ <interface> = (*type)(nil)
var _ Buffer = (*StringBuffer)(nil)
2.9 worker pool
package tasks
import "sync"
type Worker struct {
maxWorker int
ch chan func()
wg sync.WaitGroup
resMap map[string]chan any
}
func NewWorker(maxWorker int) *Worker {
w := &Worker{
maxWorker: maxWorker,
ch: make(chan func()),
resMap: make(map[string]chan any),
}
for i := 0; i < maxWorker; i++ {
go func() {
for f := range w.ch {
w.done(f)
}
}()
}
return w
}
func (w *Worker) Go(f func()) {
w.wg.Add(1)
w.ch <- f
}
func (w *Worker) Wait() {
w.wg.Wait()
close(w.ch) // 关闭通道
}
func (w *Worker) done(f func()) {
defer w.wg.Done()
f()
}
func (w *Worker) submit(f func() (res any), key string) chan any {
w.resMap[key] = make(chan any)
funcWrapper := func() {
var res = f()
w.resMap[key] <- res
close(w.resMap[key] // 关闭通道
}
w.Go(funcWrapper)
return w.resMap[key]
}
2.10 ConcurrentMap
type ConcurrentMap struct {
innerMap map[string]any
rwLock sync.RWMutex
}
func (currMap *ConcurrentMap) Get(key string) any {
currMap.rwLock.RLock()
defer currMap.rwLock.RUnlock()
return currMap.innerMap[key]
}
func (currMap *ConcurrentMap) Put(key string, value any) {
currMap.rwLock.Lock()
defer currMap.rwLock.Unlock()
currMap.innerMap[key] = value
}
func (currMap *ConcurrentMap) PutIfAbsent(key string, supplier func() any) {
currMap.rwLock.Lock()
defer currMap.rwLock.Unlock()
if value := currMap.innerMap[key]; value != nil {
return
}
currMap.innerMap[key] = supplier()
}
func NewCurrMap() *ConcurrentMap {
return &ConcurrentMap{
innerMap: make(map[string]any),
}
}
func (currMap *ConcurrentMap) getOrDefault(key string, supplier func() any) any {
currMap.rwLock.RLock()
defer currMap.rwLock.RUnlock()
if value := currMap.innerMap[key]; value != nil {
return value
}
return supplier()
}
2.11 三元表达式实现
func Ternary[T any] (condition bool, ifOutPut, elseOutPut T) T {
if condition
return ifOutput
return elseOutPut
}