语法相关

defer

defer的执行时机为归属的函数退出释放资源时执行,deferFunctionList是一个栈结构,先defer的最后执行。

  1. func fileSize(filename string) int64{
  2. f, err := os.Open(filename)
  3. if err != nil {
  4. return 0
  5. }
  6. defer f.Close()
  7. info, err := f.Stat()
  8. if err != nil {
  9. // 触发defer
  10. return 0
  11. }
  12. size := info.Size()
  13. //触发defer
  14. return size
  15. }

context

// 普通context调用
ctx, cancel := context.WithCancel(context.Background())

// 带超时的context调用
ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)

// 多个goroutine传值
ctx := context.WithValue(context.Background(), key, value)
  • 普通context ```go func run(ctx context.Context, id int) { for{
      select {
          case <-ctx.Done():
          fmt.Printf("任务结束")
          return
          default:
          fmt.Printf("任务执行中")
          time.Sleep(time.Second * 2)
      }
    
    } }

func main(){ ctx, cancel := context.WithCancel(context.Background()) go run(ctx,1) go run(ctx,2) time.Sleep(time.Second * 10) cancel() return }


- 超时context
```go
func coroutine(ctx context.Context, duration timeDuration,id int, wg *sync.WaitGroup){
    for {
        select{
            case <-ctx.Done():
            fmt.Printf("协程退出")
            wg.Done()
            return
            case <-time.After(duration):
            fmt.Printf("消息来自协程")
        }
    }
}

func main(){
    // WaitGroup等待所有的goroutine执行完毕,收到<-ctx.Done()中至信好后使wg中需要等待的goroutine数量减1
    wg:= &sync.WaitGroup{}
    ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)
    defer cancel()
    for i:=0;i<3;i++{
        wg.Add(1)
        go coroutine(ctx, 1 *time.Second, i, wg)
    }
}

指针

*p++   //只增加p指向的变量的值,不改变p指针
*p = 4 // 实现间接赋值

只执行一次

type Singleton struct {

}

var singleInstance *Singleton
var once sync.Once

func GetSingletonObj() *Singleton{
    once.Do(func(){
        fmt.Println("Create Obj")
        singleInstance = new(Singleton)
    })
    return singleInstance
}

func TestGetSingleObj(t *testing.T){
    var wg sync.WaitGroup
    for i:=0;i<10;i++{
        wg.Add(1)
        go func(){
            obj := GetSingletonObj()
            wg.Done()
        }()
        wg.Wait()
    }
}

对象池

type ReusableObj struct{

}

type ObjPool struct{
    bufChan chan *ReusableObj
}

// 初始化缓冲池
func InitObjPool(numOfObj int) *ObjPool {
    objPool := ObjPool{}
    objPool.bufChan = make(chan *ReleaseObj, numOfObj)
    for i:0; i<numOfObj;i++{
        objPool.bufChan <- &ReusableObj{}
    }
    return &objPool
}

// 获取对象
func (pool *ObjPool) GetObj(timeout time.Duration)(*ReleaseObj, err){
    select{
    case ret := <- pool.bufChan:
        return ret, nil
    case <- time.After(timeout):
        return nil, errors.New('timeout')
    }

}

func (p *ObjPool) ReleaseObj(obj *ReusableObj) error{
    select{
    case p.bufChan <- obj:
        return nil
    default:
        return errors.New('overflow')
    }

}

func TestObjPool(t *testing.T){
    pool := InitObjPool(10)
    for i:=0;i<11;i++{
        if v, err  pool.GetObj(time.Second * 1); err != nil{
            t.Error(err)
        }else{
            if err := pool.ReleaseObj(v); err != nil{
                t.Error(err)
            }
        }
    }
}

类型断言

PrimaryExpression.(Type)
v, ok = a.(T)
// ok必须得有,否则断言失败会panic

错误处理

  1. 不相信外部输入
  2. 先进行异常处理

var ( 
    ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
    ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune") 
    ErrBufferFull = errors.New("bufio: buffer full")
    ErrNegativeCount = errors.New("bufio: negative count")
)

data, err := b.Peek(1)
if err != nil {
    switch err {
    case bufio.ErrNegativeCount:
        // ... ...
        return
    case bufio.ErrBufferFull:
        // ... ...
        return
    case bufio.ErrInvalidUnreadByte:
        // ... ...
        return
    default:
        // ... ...
        return
    }
}


// 使用errors.Is
if errors.Is(err, ErrOutOfBounds) {
    // 越界的错误处理
}

channel

对同一个无缓冲 channel,只有对它进行接收操作的 Goroutine 和对它进行发送操作的 Goroutine 都存在的情况下,通信才能得以进行,否则单方面的操作会让对应的 Goroutine 陷入挂起状态。对无缓冲 channel 类型的发送与接收操作,一定要放在两个不同的 Goroutine 中进行,否则会导致 deadlock。

  • 无缓冲 channel 用作信号传递的时候,有两种情况,分别是 1 对 1 通知信号和 1 对 n 通知信号。我们先来分析下 1 对 1 通知信号这种情况。 ```go

type signal struct{}

func worker() { println(“worker is working…”) time.Sleep(1 * time.Second) }

func spawn(f func()) <-chan signal { c := make(chan signal) go func() { println(“worker start to work…”) f() c <- signal{} }() return c }

func main() { println(“start a worker…”) c := spawn(worker) <-c fmt.Println(“worker work done!”) } ```